]> git.basschouten.com Git - openhab-addons.git/commitdiff
[solax] Support for Solax EV charger via direct(local) data retrieval (#17055)
authorKonstantin Polihronov <polychronov@gmail.com>
Tue, 23 Jul 2024 07:23:03 +0000 (10:23 +0300)
committerGitHub <noreply@github.com>
Tue, 23 Jul 2024 07:23:03 +0000 (09:23 +0200)
* Implementation of Solax EV Charger support

Signed-off-by: Konstantin Polihronov <polychronov@gmail.com>
49 files changed:
bundles/org.openhab.binding.solax/README.md
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxBindingConstants.java
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxHandlerFactory.java
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/rawdata/local/LocalConnectRawDataBean.java
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/AbstractSolaxHandler.java
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessAbstractHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessChargerHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessHandler.java [deleted file]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessInverterHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/InverterType.java
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/CommonLocalDeviceData.java [new file with mode: 0644]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/CommonLocalInverterData.java [deleted file]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/EvChargerData.java [new file with mode: 0644]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/LocalData.java [new file with mode: 0644]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/LocalInverterData.java [deleted file]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/RawDataParser.java [new file with mode: 0644]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X1BoostAirMiniData.java [new file with mode: 0644]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X1BoostAirMiniInverterData.java [deleted file]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X1HybridG4Data.java [new file with mode: 0644]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X1HybridG4InverterData.java [deleted file]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X3HybridG4Data.java [new file with mode: 0644]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X3HybridG4InverterData.java [deleted file]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X3MicOrProG2Data.java [new file with mode: 0644]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X3MicOrProG2InverterData.java [deleted file]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/evchargers/EvChargerDataParser.java [new file with mode: 0644]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/inverters/X1BoostAirMiniDataParser.java [new file with mode: 0644]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/inverters/X1HybridG4DataParser.java [new file with mode: 0644]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/inverters/X3HybridG4DataParser.java [new file with mode: 0644]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/inverters/X3MicOrProG2DataParser.java [new file with mode: 0644]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/RawDataParser.java [deleted file]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/X1BoostAirMiniDataParser.java [deleted file]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/X1HybridG4DataParser.java [deleted file]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/X3HybridG4DataParser.java [deleted file]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/X3MicOrProG2DataParser.java [deleted file]
bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/util/ByteUtil.java [new file with mode: 0644]
bundles/org.openhab.binding.solax/src/main/resources/OH-INF/i18n/solax.properties
bundles/org.openhab.binding.solax/src/main/resources/OH-INF/thing/channel_types.xml
bundles/org.openhab.binding.solax/src/main/resources/OH-INF/thing/localConnectEVCharger.xml [new file with mode: 0644]
bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/AbstractParserTest.java
bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/TestX1BoostAirMiniDataParser.java [deleted file]
bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/TestX1HybridG4Parser.java [deleted file]
bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/TestX3HybridG4Parser.java [deleted file]
bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/TestX3MicOrProG2Parser.java [deleted file]
bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestEVChargerParser.java [new file with mode: 0644]
bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestX1BoostAirMiniDataParser.java [new file with mode: 0644]
bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestX1HybridG4Parser.java [new file with mode: 0644]
bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestX3HybridG4Parser.java [new file with mode: 0644]
bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestX3MicOrProG2Parser.java [new file with mode: 0644]
bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/util/TestByteUtil.java [new file with mode: 0644]

index 08b6cd8973791170463c84ceccb7d68c321648cd..a928d872dfa8132171468874ee94ffb7219c27dd 100644 (file)
@@ -12,10 +12,11 @@ In case the parsed information that comes with the binding out of the box differ
 
 ## Supported Things
 
-| Thing                  | Thing Type | Description                                                                         |
-|------------------------|------------|-------------------------------------------------------------------------------------|
-| local-connect-inverter | Thing      | An inverter representation with all the data available as a channels (directly retrieved from the wi-fi module  |
-| cloud-connect-inverter | Thing      | An inverter representation with all the data available as a channels (retrieved from the Solax cloud API)  |
+| Thing                  | Thing Type | Description                                                                                                                     |
+|------------------------|------------|---------------------------------------------------------------------------------------------------------------------------------|
+| local-connect-inverter | Thing      | An inverter representation with all the data available as a channels (directly retrieved from the wi-fi module)                 |
+| local-connect-charger  | Thing      | An electric vehicle charger representation with all the data available as a channels (directly retrieved from the wi-fi module) |
+| cloud-connect-inverter | Thing      | An inverter representation with all the data available as a channels (retrieved from the Solax cloud API)                       |
 
 Note: Channels may vary depending on the inverter type and the availability of information for parsing the raw data. 
 If you're missing a channel this means that it's not supported for your inverter type.
@@ -113,6 +114,46 @@ If you're missing a channel this means that it's not supported for your inverter
 | inverterType      | Inverter Type (for example X1_HYBRID_G4)  |
 
 
+### Local Connect EV Charger Configuration
+
+### Parameters
+
+| Parameter         | Description                                                                                                                                        |
+|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------|
+| refreshInterval   | Defines the refresh interval when the binding polls from the inverter's Wi-Fi module (in seconds). Optional parameter. Default 10 seconds.         |
+| password          | Password for accessing the Wi-Fi module (the serial number of the wifi). Mandatory parameter.                                                      |
+| hostname          | IP address or hostname of your Wi-Fi module. If hostname is used must be resolvable by OpenHAB. Mandatory parameter.                               |
+
+### Channels
+
+| Channel ID                               | Type                        | Description                                                   |
+|------------------------------------------|-----------------------------|---------------------------------------------------------------|
+| eq-single-session                        | Number:Energy               | Energy charged for the current session. [kWh]                 |
+| eq-total                                 | Number:Energy               | Total energy charged for all sessions. [kWh]                  |
+| charger-output-power-phase1              | Number:Power                | Power to/from the charger phase 1. [W]                        |
+| charger-output-power-phase2              | Number:Power                | Power to/from the charger phase 2. [W]                        |
+| charger-output-power-phase3              | Number:Power                | Power to/from the charger phase 3. [W]                        |
+| charger-total-output-power               | Number:Power                | Power from the charger on all phases. [W]                     |
+| charger-current-phase1                   | Number:ElectricCurrent      | Current from the charger phase 1. [A]                         |
+| charger-current-phase2                   | Number:ElectricCurrent      | Current from the charger phase 2. [A]                         |
+| charger-current-phase3                   | Number:ElectricCurrent      | Current from the charger phase 3. [A]                         |
+| charger-voltage-phase1                   | Number:ElectricPotential    | Voltage of the charger's phase 1. [V]                         |
+| charger-voltage-phase2                   | Number:ElectricPotential    | Voltage of the charger's phase 2. [V]                         |
+| charger-voltage-phase3                   | Number:ElectricPotential    | Voltage of the charger's phase 3. [V]                         |
+| charger-external-current-phase1          | Number:ElectricCurrent      | Current from the provider phase 1. [A]                        |
+| charger-external-current-phase2          | Number:ElectricCurrent      | Current from the provider phase 2. [A]                        |
+| charger-external-current-phase3          | Number:ElectricCurrent      | Current from the provider phase 3. [A]                        |
+| charger-external-power-phase1            | Number:Power                | Power from the provider phase 1. [W]                          |
+| charger-external-power-phase2            | Number:Power                | Power from the provider phase 2. [W]                          |
+| charger-external-power-phase3            | Number:Power                | Power from the provider phase 3. [W]                          |
+| charger-external-total-power             | Number:Power                | Total power from the provider. [W]                            |
+| charger-plug-temperature                 | Number:Temperature          | Temperature of the charger's plug. [°C]                       |
+| charger-internal-temperature             | Number:Temperature          | Internal temperature on the board of the charger. [°C]        |
+| charger-mode                             | String                      | Charger Workmode.                                             |
+| charger-state                            | String                      | Charger State.                                                |
+| last-update-time                         | DateTime                    | Last time with a successful retrieval of data.                |
+
+
 ### Cloud Connect Inverter Configuration
 
 | Parameter         | Description                                                                                                                                        |
index 4d2f18fb49cd8b80e684c1e9caa11113c0151ecd..16d2f2c4c5c6ff4fd6f55d08e39f47a4278735d3 100644 (file)
@@ -28,16 +28,19 @@ public class SolaxBindingConstants {
 
     public static final String BINDING_ID = "solax";
     private static final String THING_LOCAL_CONNECT_INVERTER_ID = "local-connect-inverter";
+    private static final String THING_LOCAL_CONNECT_CHARGER_ID = "local-connect-charger";
     private static final String THING_CLOUD_CONNECT_INVERTER_ID = "cloud-connect-inverter";
 
     // List of all Thing Type UIDs
     public static final ThingTypeUID THING_TYPE_LOCAL_CONNECT_INVERTER = new ThingTypeUID(BINDING_ID,
             THING_LOCAL_CONNECT_INVERTER_ID);
+    public static final ThingTypeUID THING_TYPE_LOCAL_CONNECT_CHARGER = new ThingTypeUID(BINDING_ID,
+            THING_LOCAL_CONNECT_CHARGER_ID);
     public static final ThingTypeUID THING_TYPE_CLOUD_CONNECT_INVERTER = new ThingTypeUID(BINDING_ID,
             THING_CLOUD_CONNECT_INVERTER_ID);
 
     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_LOCAL_CONNECT_INVERTER,
-            THING_TYPE_CLOUD_CONNECT_INVERTER);
+            THING_TYPE_LOCAL_CONNECT_CHARGER, THING_TYPE_CLOUD_CONNECT_INVERTER);
 
     // List of properties
     public static final String PROPERTY_INVERTER_TYPE = "inverterType";
@@ -116,6 +119,32 @@ public class SolaxBindingConstants {
     public static final String CHANNEL_INVERTER_EPS_POWER_S = "inverter-eps-power-s";
     public static final String CHANNEL_INVERTER_EPS_POWER_T = "inverter-eps-power-t";
 
+    // EV Charger channels
+    public static final String CHANNEL_CHARGER_MODE = "charger-mode";
+    public static final String CHANNEL_CHARGER_STATE = "charger-state";
+    public static final String CHANNEL_CHARGER_EQ_SINGLE_SESSION = "eq-single-session";
+    public static final String CHANNEL_CHARGER_EQ_TOTAL = "eq-total";
+    public static final String CHANNEL_CHARGER_OUTPUT_POWER_PHASE1 = "charger-output-power-phase1";
+    public static final String CHANNEL_CHARGER_OUTPUT_POWER_PHASE2 = "charger-output-power-phase2";
+    public static final String CHANNEL_CHARGER_OUTPUT_POWER_PHASE3 = "charger-output-power-phase3";
+    public static final String CHANNEL_CHARGER_TOTAL_OUTPUT_POWER = "charger-total-output-power";
+    public static final String CHANNEL_CHARGER_OUTPUT_CURRENT_PHASE1 = "charger-current-phase1";
+    public static final String CHANNEL_CHARGER_OUTPUT_CURRENT_PHASE2 = "charger-current-phase2";
+    public static final String CHANNEL_CHARGER_OUTPUT_CURRENT_PHASE3 = "charger-current-phase3";
+    public static final String CHANNEL_CHARGER_OUTPUT_VOLTAGE_PHASE1 = "charger-voltage-phase1";
+    public static final String CHANNEL_CHARGER_OUTPUT_VOLTAGE_PHASE2 = "charger-voltage-phase2";
+    public static final String CHANNEL_CHARGER_OUTPUT_VOLTAGE_PHASE3 = "charger-voltage-phase3";
+    public static final String CHANNEL_CHARGER_EXTERNAL_CURRENT_PHASE1 = "charger-external-current-phase1";
+    public static final String CHANNEL_CHARGER_EXTERNAL_CURRENT_PHASE2 = "charger-external-current-phase2";
+    public static final String CHANNEL_CHARGER_EXTERNAL_CURRENT_PHASE3 = "charger-external-current-phase3";
+    public static final String CHANNEL_CHARGER_EXTERNAL_POWER_PHASE1 = "charger-external-power-phase1";
+    public static final String CHANNEL_CHARGER_EXTERNAL_POWER_PHASE2 = "charger-external-power-phase2";
+    public static final String CHANNEL_CHARGER_EXTERNAL_POWER_PHASE3 = "charger-external-power-phase3";
+    public static final String CHANNEL_CHARGER_TOTAL_EXTERNAL_POWER = "charger-external-total-power";
+    public static final String CHANNEL_CHARGER_PLUG_TEMPERATURE = "charger-plug-temperature";
+    public static final String CHANNEL_CHARGER_INTERNAL_TEMPERATURE = "charger-internal-temperature";
+
     // I18N Keys
     public static final String I18N_KEY_OFFLINE_COMMUNICATION_ERROR_JSON_CANNOT_BE_RETRIEVED = "@text/offline.communication-error.json-cannot-be-retrieved";
+    public static final String I18N_KEY_OFFLINE_CONFIGURATION_ERROR_JSON_CANNOT_BE_PARSED = "@text/offline.configuration-error.json-cannot-be-parsed";
 }
index c0c5a2b1035b48c97b3348300bfe428d306fbe10..1a2fe28d4f426939ec3057f6b1dc13082d5e07a8 100644 (file)
@@ -17,7 +17,8 @@ import static org.openhab.binding.solax.internal.SolaxBindingConstants.*;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.solax.internal.handlers.SolaxCloudHandler;
-import org.openhab.binding.solax.internal.handlers.SolaxLocalAccessHandler;
+import org.openhab.binding.solax.internal.handlers.SolaxLocalAccessChargerHandler;
+import org.openhab.binding.solax.internal.handlers.SolaxLocalAccessInverterHandler;
 import org.openhab.core.i18n.LocaleProvider;
 import org.openhab.core.i18n.TimeZoneProvider;
 import org.openhab.core.i18n.TranslationProvider;
@@ -61,9 +62,11 @@ public class SolaxHandlerFactory extends BaseThingHandlerFactory {
     protected @Nullable ThingHandler createHandler(Thing thing) {
         ThingTypeUID thingTypeUID = thing.getThingTypeUID();
         if (THING_TYPE_LOCAL_CONNECT_INVERTER.equals(thingTypeUID)) {
-            return new SolaxLocalAccessHandler(thing, i18nProvider, timeZoneProvider);
+            return new SolaxLocalAccessInverterHandler(thing, i18nProvider, timeZoneProvider);
         } else if (THING_TYPE_CLOUD_CONNECT_INVERTER.equals(thingTypeUID)) {
             return new SolaxCloudHandler(thing, i18nProvider, timeZoneProvider, localeProvider);
+        } else if (THING_TYPE_LOCAL_CONNECT_CHARGER.equals(thingTypeUID)) {
+            return new SolaxLocalAccessChargerHandler(thing, i18nProvider, timeZoneProvider);
         }
         return null;
     }
index 876648493c614efabc1356613844f4aa77a581a0..576853d321e3c37c731913a5a471a881baa78cc1 100644 (file)
@@ -30,6 +30,7 @@ import com.google.gson.annotations.SerializedName;
 @NonNullByDefault
 public class LocalConnectRawDataBean implements RawDataBean {
 
+    @SerializedName(value = "sn", alternate = { "SN" })
     private @Nullable String sn;
     private @Nullable String ver;
     private int type;
@@ -38,6 +39,10 @@ public class LocalConnectRawDataBean implements RawDataBean {
     @SerializedName("Information")
     private String @Nullable [] information;
     private @Nullable String rawData;
+    @SerializedName("OCPPServer")
+    private @Nullable String ocppServer;
+    @SerializedName("OCPPChargerId")
+    private @Nullable String ocppChargerId;
 
     @Override
     public String toString() {
@@ -95,6 +100,22 @@ public class LocalConnectRawDataBean implements RawDataBean {
         this.rawData = rawData;
     }
 
+    public @Nullable String getOcppServer() {
+        return ocppServer;
+    }
+
+    public void setOcppServer(@Nullable String ocppServer) {
+        this.ocppServer = ocppServer;
+    }
+
+    public @Nullable String getOcppChargerId() {
+        return ocppChargerId;
+    }
+
+    public void setOcppChargerId(String ocppChargerId) {
+        this.ocppChargerId = ocppChargerId;
+    }
+
     public static LocalConnectRawDataBean fromJson(String json) {
         if (json.isEmpty()) {
             throw new IllegalArgumentException("JSON payload should not be empty");
index 037efa896bb4cf20069749c1ac9a1171d1b3b287..cfcb9d8143c4daec781aaad1fead0bf08cced516 100644 (file)
@@ -36,6 +36,8 @@ import org.openhab.core.types.RefreshType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.gson.JsonSyntaxException;
+
 /**
  * The {@link SolaxCloudHandler} is responsible for handling commands, which are
  * sent to one of the channels.
@@ -99,6 +101,10 @@ public abstract class AbstractSolaxHandler extends BaseThingHandler {
                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
             } catch (SolaxUpdateException e) {
                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
+            } catch (JsonSyntaxException jse) {
+                logger.debug("JsonSyntaxException received.", jse);
+                updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+                        SolaxBindingConstants.I18N_KEY_OFFLINE_CONFIGURATION_ERROR_JSON_CANNOT_BE_PARSED);
             } finally {
                 retrieveDataCallLock.unlock();
             }
@@ -116,6 +122,14 @@ public abstract class AbstractSolaxHandler extends BaseThingHandler {
         }
     }
 
+    @Override
+    protected void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description) {
+        super.updateStatus(status, statusDetail, description);
+        if (status == ThingStatus.OFFLINE && statusDetail == ThingStatusDetail.CONFIGURATION_ERROR) {
+            cancelSchedule();
+        }
+    }
+
     @Override
     public void dispose() {
         super.dispose();
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessAbstractHandler.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessAbstractHandler.java
new file mode 100644 (file)
index 0000000..39d55a5
--- /dev/null
@@ -0,0 +1,101 @@
+/**
+ * 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.handlers;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.measure.Quantity;
+import javax.measure.Unit;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.solax.internal.SolaxConfiguration;
+import org.openhab.binding.solax.internal.connectivity.LocalHttpConnector;
+import org.openhab.binding.solax.internal.connectivity.SolaxConnector;
+import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean;
+import org.openhab.core.i18n.TimeZoneProvider;
+import org.openhab.core.i18n.TranslationProvider;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.thing.Channel;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.types.UnDefType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link SolaxLocalAccessAbstractHandler} abstract handler combining the common logic for an inverter and a charger
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public abstract class SolaxLocalAccessAbstractHandler extends AbstractSolaxHandler {
+    private final Logger logger = LoggerFactory.getLogger(SolaxLocalAccessAbstractHandler.class);
+
+    protected final Set<String> unsupportedExistingChannels = new HashSet<>();
+    protected boolean alreadyRemovedUnsupportedChannels;
+
+    public SolaxLocalAccessAbstractHandler(Thing thing, TranslationProvider i18nProvider,
+            TimeZoneProvider timeZoneProvider) {
+        super(thing, i18nProvider, timeZoneProvider);
+    }
+
+    @Override
+    protected SolaxConnector createConnector(SolaxConfiguration config) {
+        return new LocalHttpConnector(config.password, config.hostname);
+    }
+
+    protected LocalConnectRawDataBean parseJson(String rawJsonData) {
+        LocalConnectRawDataBean fromJson = LocalConnectRawDataBean.fromJson(rawJsonData);
+        logger.debug("Received a new inverter JSON object. Data = {}", fromJson.toString());
+        return fromJson;
+    }
+
+    protected void removeUnsupportedChannels(Set<String> supportedChannels) {
+        if (supportedChannels.isEmpty()) {
+            return;
+        }
+        List<Channel> channels = getThing().getChannels();
+        List<Channel> channelsToRemove = channels.stream()
+                .filter(channel -> !supportedChannels.contains(channel.getUID().getId())).toList();
+
+        if (!channelsToRemove.isEmpty()) {
+            if (logger.isDebugEnabled()) {
+                logRemovedChannels(channelsToRemove);
+            }
+            updateThing(editThing().withoutChannels(channelsToRemove).build());
+        }
+    }
+
+    private void logRemovedChannels(List<Channel> channelsToRemove) {
+        List<String> channelsToRemoveForLog = channelsToRemove.stream().map(channel -> channel.getUID().getId())
+                .toList();
+        logger.debug("Detected unsupported channels for the current inverter. Channels to be removed: {}",
+                channelsToRemoveForLog);
+    }
+
+    protected <T extends Quantity<T>> void updateChannel(String channelID, double value, Unit<T> unit,
+            Set<String> supportedChannels) {
+        if (supportedChannels.contains(channelID)) {
+            if (value > Short.MIN_VALUE) {
+                updateState(channelID, new QuantityType<>(value, unit));
+            } else if (!unsupportedExistingChannels.contains(channelID)) {
+                updateState(channelID, UnDefType.UNDEF);
+                unsupportedExistingChannels.add(channelID);
+                logger.warn(
+                        "Channel {} is marked as supported, but its value is out of the defined range. Value = {}. This is unexpected behaviour. Please file a bug.",
+                        channelID, value);
+            }
+        }
+    }
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessChargerHandler.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessChargerHandler.java
new file mode 100644 (file)
index 0000000..5609c43
--- /dev/null
@@ -0,0 +1,129 @@
+/**
+ * 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.handlers;
+
+import java.time.ZonedDateTime;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.solax.internal.SolaxBindingConstants;
+import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean;
+import org.openhab.binding.solax.internal.exceptions.SolaxUpdateException;
+import org.openhab.binding.solax.internal.model.local.EvChargerData;
+import org.openhab.binding.solax.internal.model.local.LocalData;
+import org.openhab.binding.solax.internal.model.local.RawDataParser;
+import org.openhab.binding.solax.internal.model.local.evchargers.EvChargerDataParser;
+import org.openhab.core.i18n.TimeZoneProvider;
+import org.openhab.core.i18n.TranslationProvider;
+import org.openhab.core.library.types.DateTimeType;
+import org.openhab.core.library.types.StringType;
+import org.openhab.core.library.unit.SIUnits;
+import org.openhab.core.library.unit.Units;
+import org.openhab.core.thing.Thing;
+
+/**
+ * The {@link SolaxLocalAccessChargerHandler} the handler for the charger
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public class SolaxLocalAccessChargerHandler extends SolaxLocalAccessAbstractHandler {
+
+    private static final EvChargerDataParser parser = new EvChargerDataParser();
+
+    public SolaxLocalAccessChargerHandler(Thing thing, TranslationProvider i18nProvider,
+            TimeZoneProvider timeZoneProvider) {
+        super(thing, i18nProvider, timeZoneProvider);
+    }
+
+    @Override
+    public void initialize() {
+        removeUnsupportedChannels(parser.getSupportedChannels());
+        super.initialize();
+    }
+
+    @Override
+    protected void updateFromData(String rawJsonData) throws SolaxUpdateException {
+        LocalConnectRawDataBean rawDataBean = parseJson(rawJsonData);
+        EvChargerData data = parser.getData(rawDataBean);
+        updateChannels(parser, data);
+        updateProperties(data);
+    }
+
+    private void updateProperties(LocalData data) {
+    }
+
+    private void updateChannels(RawDataParser parser, EvChargerData data) {
+        Set<String> supportedChannels = parser.getSupportedChannels();
+
+        // States/modes
+        updateState(SolaxBindingConstants.CHANNEL_CHARGER_MODE, new StringType(data.getDeviceMode()));
+        updateState(SolaxBindingConstants.CHANNEL_CHARGER_STATE, new StringType(data.getDeviceState()));
+
+        // Energy
+        updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_EQ_SINGLE_SESSION, data.getEqSingle(), Units.KILOWATT_HOUR,
+                supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_EQ_TOTAL, data.getEqTotal(), Units.KILOWATT_HOUR,
+                supportedChannels);
+
+        // Output data
+        updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_OUTPUT_CURRENT_PHASE1, data.getCurrentPhase1(),
+                Units.AMPERE, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_OUTPUT_CURRENT_PHASE2, data.getCurrentPhase2(),
+                Units.AMPERE, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_OUTPUT_CURRENT_PHASE3, data.getCurrentPhase3(),
+                Units.AMPERE, supportedChannels);
+
+        updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_OUTPUT_VOLTAGE_PHASE1, data.getVoltagePhase1(), Units.VOLT,
+                supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_OUTPUT_VOLTAGE_PHASE2, data.getVoltagePhase2(), Units.VOLT,
+                supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_OUTPUT_VOLTAGE_PHASE3, data.getVoltagePhase3(), Units.VOLT,
+                supportedChannels);
+
+        updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_OUTPUT_POWER_PHASE1, data.getOutputPowerPhase1(),
+                Units.WATT, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_OUTPUT_POWER_PHASE2, data.getOutputPowerPhase2(),
+                Units.WATT, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_OUTPUT_POWER_PHASE3, data.getOutputPowerPhase3(),
+                Units.WATT, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_TOTAL_OUTPUT_POWER, data.getTotalChargePower(), Units.WATT,
+                supportedChannels);
+
+        // Provider data
+        updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_EXTERNAL_CURRENT_PHASE1, data.getExternalCurrentPhase1(),
+                Units.AMPERE, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_EXTERNAL_CURRENT_PHASE2, data.getExternalCurrentPhase2(),
+                Units.AMPERE, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_EXTERNAL_CURRENT_PHASE3, data.getExternalCurrentPhase3(),
+                Units.AMPERE, supportedChannels);
+
+        updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_EXTERNAL_POWER_PHASE1, data.getExternalPowerPhase1(),
+                Units.WATT, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_EXTERNAL_POWER_PHASE2, data.getExternalPowerPhase2(),
+                Units.WATT, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_EXTERNAL_POWER_PHASE3, data.getExternalPowerPhase3(),
+                Units.WATT, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_TOTAL_EXTERNAL_POWER, data.getExternalTotalPower(),
+                Units.WATT, supportedChannels);
+
+        // Temperatures
+        updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_PLUG_TEMPERATURE, data.getPlugTemperature(),
+                SIUnits.CELSIUS, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_CHARGER_INTERNAL_TEMPERATURE, data.getInternalTemperature(),
+                SIUnits.CELSIUS, supportedChannels);
+
+        // Binding provided data
+        updateState(SolaxBindingConstants.CHANNEL_TIMESTAMP, new DateTimeType(ZonedDateTime.now()));
+    }
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessHandler.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessHandler.java
deleted file mode 100644 (file)
index 60f59e7..0000000
+++ /dev/null
@@ -1,263 +0,0 @@
-/**
- * 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.handlers;
-
-import java.time.ZonedDateTime;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import javax.measure.Quantity;
-import javax.measure.Unit;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.solax.internal.SolaxBindingConstants;
-import org.openhab.binding.solax.internal.SolaxConfiguration;
-import org.openhab.binding.solax.internal.connectivity.LocalHttpConnector;
-import org.openhab.binding.solax.internal.connectivity.SolaxConnector;
-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;
-import org.openhab.core.i18n.TimeZoneProvider;
-import org.openhab.core.i18n.TranslationProvider;
-import org.openhab.core.library.types.DateTimeType;
-import org.openhab.core.library.types.QuantityType;
-import org.openhab.core.library.types.StringType;
-import org.openhab.core.library.unit.SIUnits;
-import org.openhab.core.library.unit.Units;
-import org.openhab.core.thing.Channel;
-import org.openhab.core.thing.Thing;
-import org.openhab.core.thing.ThingStatus;
-import org.openhab.core.thing.ThingStatusDetail;
-import org.openhab.core.types.UnDefType;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.gson.JsonParseException;
-
-/**
- * The {@link SolaxLocalAccessHandler} is responsible for handling commands, which are
- * sent to one of the channels.
- *
- * @author Konstantin Polihronov - Initial contribution
- */
-@NonNullByDefault
-public class SolaxLocalAccessHandler extends AbstractSolaxHandler {
-
-    private final Logger logger = LoggerFactory.getLogger(SolaxLocalAccessHandler.class);
-
-    private boolean alreadyRemovedUnsupportedChannels;
-
-    private final Set<String> unsupportedExistingChannels = new HashSet<>();
-
-    public SolaxLocalAccessHandler(Thing thing, TranslationProvider i18nProvider, TimeZoneProvider timeZoneProvider) {
-        super(thing, i18nProvider, timeZoneProvider);
-    }
-
-    @Override
-    protected SolaxConnector createConnector(SolaxConfiguration config) {
-        return new LocalHttpConnector(config.password, config.hostname);
-    }
-
-    @Override
-    protected void updateFromData(String rawJsonData) {
-        try {
-            LocalConnectRawDataBean rawDataBean = parseJson(rawJsonData);
-            InverterType inverterType = calculateInverterType(rawDataBean);
-            RawDataParser parser = inverterType.getParser();
-            if (parser != null) {
-                if (!alreadyRemovedUnsupportedChannels) {
-                    removeUnsupportedChannels(inverterType.getSupportedChannels());
-                    alreadyRemovedUnsupportedChannels = true;
-                }
-
-                LocalInverterData genericInverterData = parser.getData(rawDataBean);
-                updateChannels(parser, genericInverterData);
-                updateProperties(genericInverterData);
-            } else {
-                cancelSchedule();
-                updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
-                        "@text/offline.configuration-error.parser-not-implemented [\"" + inverterType.name() + "\"]");
-            }
-        } catch (JsonParseException e) {
-            logger.debug("Unable to deserialize from JSON.", e);
-            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
-        }
-    }
-
-    private LocalConnectRawDataBean parseJson(String rawJsonData) {
-        LocalConnectRawDataBean fromJson = LocalConnectRawDataBean.fromJson(rawJsonData);
-        logger.debug("Received a new inverter JSON object. Data = {}", fromJson.toString());
-        return fromJson;
-    }
-
-    private InverterType calculateInverterType(LocalConnectRawDataBean rawDataBean) {
-        int type = rawDataBean.getType();
-        return InverterType.fromIndex(type);
-    }
-
-    private void updateProperties(LocalInverterData genericInverterData) {
-        updateProperty(Thing.PROPERTY_SERIAL_NUMBER, genericInverterData.getWifiSerial());
-        updateProperty(SolaxBindingConstants.PROPERTY_INVERTER_TYPE, genericInverterData.getInverterType().name());
-    }
-
-    private void updateChannels(RawDataParser parser, LocalInverterData inverterData) {
-        updateState(SolaxBindingConstants.CHANNEL_RAW_DATA, new StringType(inverterData.getRawData()));
-
-        Set<String> supportedChannels = parser.getSupportedChannels();
-        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_PV1_POWER, inverterData.getPV1Power(), Units.WATT,
-                supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_PV1_CURRENT, inverterData.getPV1Current(), Units.AMPERE,
-                supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_PV1_VOLTAGE, inverterData.getPV1Voltage(), Units.VOLT,
-                supportedChannels);
-
-        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_PV2_POWER, inverterData.getPV2Power(), Units.WATT,
-                supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_PV2_CURRENT, inverterData.getPV2Current(), Units.AMPERE,
-                supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_PV2_VOLTAGE, inverterData.getPV2Voltage(), Units.VOLT,
-                supportedChannels);
-
-        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_PV_TOTAL_POWER, inverterData.getPVTotalPower(), Units.WATT,
-                supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_PV_TOTAL_CURRENT, inverterData.getPVTotalCurrent(),
-                Units.AMPERE, supportedChannels);
-
-        updateChannel(SolaxBindingConstants.CHANNEL_BATTERY_POWER, inverterData.getBatteryPower(), Units.WATT,
-                supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_BATTERY_CURRENT, inverterData.getBatteryCurrent(), Units.AMPERE,
-                supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_BATTERY_VOLTAGE, inverterData.getBatteryVoltage(), Units.VOLT,
-                supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_BATTERY_TEMPERATURE, inverterData.getBatteryTemperature(),
-                SIUnits.CELSIUS, supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_BATTERY_STATE_OF_CHARGE, inverterData.getBatteryLevel(),
-                Units.PERCENT, supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_FEED_IN_POWER, inverterData.getFeedInPower(), Units.WATT,
-                supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_POWER_USAGE, inverterData.getPowerUsage(), Units.WATT,
-                supportedChannels);
-        updateState(SolaxBindingConstants.CHANNEL_INVERTER_WORKMODE,
-                new StringType(inverterData.getInverterWorkMode()));
-
-        // Totals
-        updateChannel(SolaxBindingConstants.CHANNEL_TOTAL_ENERGY, inverterData.getTotalEnergy(), Units.KILOWATT_HOUR,
-                supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_TOTAL_BATTERY_DISCHARGE_ENERGY,
-                inverterData.getTotalBatteryDischargeEnergy(), Units.KILOWATT_HOUR, supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_TOTAL_BATTERY_CHARGE_ENERGY,
-                inverterData.getTotalBatteryChargeEnergy(), Units.KILOWATT_HOUR, supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_TOTAL_PV_ENERGY, inverterData.getTotalPVEnergy(),
-                Units.KILOWATT_HOUR, supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_TOTAL_FEED_IN_ENERGY, inverterData.getTotalFeedInEnergy(),
-                Units.KILOWATT_HOUR, supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_TOTAL_CONSUMPTION, inverterData.getTotalConsumption(),
-                Units.KILOWATT_HOUR, supportedChannels);
-
-        // Today's
-        updateChannel(SolaxBindingConstants.CHANNEL_TODAY_ENERGY, inverterData.getTodayEnergy(), Units.KILOWATT_HOUR,
-                supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_TODAY_BATTERY_DISCHARGE_ENERGY,
-                inverterData.getTodayBatteryDischargeEnergy(), Units.KILOWATT_HOUR, supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_TODAY_BATTERY_CHARGE_ENERGY,
-                inverterData.getTodayBatteryChargeEnergy(), Units.KILOWATT_HOUR, supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_TODAY_FEED_IN_ENERGY, inverterData.getTodayFeedInEnergy(),
-                Units.KILOWATT_HOUR, supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_TODAY_CONSUMPTION, inverterData.getTodayConsumption(),
-                Units.KILOWATT_HOUR, supportedChannels);
-
-        // Single phase specific channels
-        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_POWER, inverterData.getInverterOutputPower(),
-                Units.WATT, supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_CURRENT, inverterData.getInverterCurrent(),
-                Units.AMPERE, supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_VOLTAGE, inverterData.getInverterVoltage(),
-                Units.VOLT, supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_FREQUENCY, inverterData.getInverterFrequency(),
-                Units.HERTZ, supportedChannels);
-
-        // Three phase specific channels
-        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_POWER_PHASE1, inverterData.getOutputPowerPhase1(),
-                Units.WATT, supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_POWER_PHASE2, inverterData.getOutputPowerPhase2(),
-                Units.WATT, supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_POWER_PHASE3, inverterData.getOutputPowerPhase3(),
-                Units.WATT, supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_TOTAL_OUTPUT_POWER, inverterData.getTotalOutputPower(),
-                Units.WATT, supportedChannels);
-
-        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_CURRENT_PHASE1, inverterData.getCurrentPhase1(),
-                Units.AMPERE, supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_CURRENT_PHASE2, inverterData.getCurrentPhase2(),
-                Units.AMPERE, supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_CURRENT_PHASE3, inverterData.getCurrentPhase3(),
-                Units.AMPERE, supportedChannels);
-
-        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_VOLTAGE_PHASE1, inverterData.getVoltagePhase1(),
-                Units.VOLT, supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_VOLTAGE_PHASE2, inverterData.getVoltagePhase2(),
-                Units.VOLT, supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_VOLTAGE_PHASE3, inverterData.getVoltagePhase3(),
-                Units.VOLT, supportedChannels);
-
-        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_FREQUENCY_PHASE1, inverterData.getFrequencyPhase1(),
-                Units.HERTZ, supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_FREQUENCY_PHASE2, inverterData.getFrequencyPhase2(),
-                Units.HERTZ, supportedChannels);
-        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_FREQUENCY_PHASE3, inverterData.getFrequencyPhase3(),
-                Units.HERTZ, supportedChannels);
-
-        // Binding provided data
-        updateState(SolaxBindingConstants.CHANNEL_TIMESTAMP, new DateTimeType(ZonedDateTime.now()));
-    }
-
-    private void removeUnsupportedChannels(Set<String> supportedChannels) {
-        if (supportedChannels.isEmpty()) {
-            return;
-        }
-        List<Channel> channels = getThing().getChannels();
-        List<Channel> channelsToRemove = channels.stream()
-                .filter(channel -> !supportedChannels.contains(channel.getUID().getId())).toList();
-
-        if (!channelsToRemove.isEmpty()) {
-            if (logger.isDebugEnabled()) {
-                logRemovedChannels(channelsToRemove);
-            }
-            updateThing(editThing().withoutChannels(channelsToRemove).build());
-        }
-    }
-
-    private void logRemovedChannels(List<Channel> channelsToRemove) {
-        List<String> channelsToRemoveForLog = channelsToRemove.stream().map(channel -> channel.getUID().getId())
-                .toList();
-        logger.debug("Detected unsupported channels for the current inverter. Channels to be removed: {}",
-                channelsToRemoveForLog);
-    }
-
-    private <T extends Quantity<T>> void updateChannel(String channelID, double value, Unit<T> unit,
-            Set<String> supportedChannels) {
-        if (supportedChannels.contains(channelID)) {
-            if (value > Short.MIN_VALUE) {
-                updateState(channelID, new QuantityType<>(value, unit));
-            } else if (!unsupportedExistingChannels.contains(channelID)) {
-                updateState(channelID, UnDefType.UNDEF);
-                unsupportedExistingChannels.add(channelID);
-                logger.warn(
-                        "Channel {} is marked as supported, but its value is out of the defined range. Value = {}. This is unexpected behaviour. Please file a bug.",
-                        channelID, value);
-            }
-        }
-    }
-}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessInverterHandler.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/handlers/SolaxLocalAccessInverterHandler.java
new file mode 100644 (file)
index 0000000..b496cc0
--- /dev/null
@@ -0,0 +1,199 @@
+/**
+ * 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.handlers;
+
+import java.time.ZonedDateTime;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.solax.internal.SolaxBindingConstants;
+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.LocalData;
+import org.openhab.binding.solax.internal.model.local.RawDataParser;
+import org.openhab.core.i18n.TimeZoneProvider;
+import org.openhab.core.i18n.TranslationProvider;
+import org.openhab.core.library.types.DateTimeType;
+import org.openhab.core.library.types.StringType;
+import org.openhab.core.library.unit.SIUnits;
+import org.openhab.core.library.unit.Units;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.JsonParseException;
+
+/**
+ * The {@link SolaxLocalAccessInverterHandler} the handler for the inverter
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public class SolaxLocalAccessInverterHandler extends SolaxLocalAccessAbstractHandler {
+
+    private final Logger logger = LoggerFactory.getLogger(SolaxLocalAccessInverterHandler.class);
+
+    public SolaxLocalAccessInverterHandler(Thing thing, TranslationProvider i18nProvider,
+            TimeZoneProvider timeZoneProvider) {
+        super(thing, i18nProvider, timeZoneProvider);
+    }
+
+    @Override
+    protected void updateFromData(String rawJsonData) {
+        try {
+            LocalConnectRawDataBean rawDataBean = parseJson(rawJsonData);
+            InverterType inverterType = calculateInverterType(rawDataBean);
+            RawDataParser parser = inverterType.getParser();
+            if (parser != null) {
+                if (!alreadyRemovedUnsupportedChannels) {
+                    removeUnsupportedChannels(inverterType.getSupportedChannels());
+                    alreadyRemovedUnsupportedChannels = true;
+                }
+
+                LocalData genericInverterData = parser.getData(rawDataBean);
+                updateChannels(parser, genericInverterData);
+                updateProperties(genericInverterData);
+            } else {
+                cancelSchedule();
+                updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+                        "@text/offline.configuration-error.parser-not-implemented [\"" + inverterType.name() + "\"]");
+            }
+        } catch (JsonParseException e) {
+            logger.debug("Unable to deserialize from JSON.", e);
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
+        }
+    }
+
+    private InverterType calculateInverterType(LocalConnectRawDataBean rawDataBean) {
+        int type = rawDataBean.getType();
+        return InverterType.fromIndex(type);
+    }
+
+    private void updateProperties(LocalData genericInverterData) {
+        updateProperty(Thing.PROPERTY_SERIAL_NUMBER, genericInverterData.getWifiSerial());
+        updateProperty(SolaxBindingConstants.PROPERTY_INVERTER_TYPE, genericInverterData.getInverterType().name());
+    }
+
+    private void updateChannels(RawDataParser parser, LocalData inverterData) {
+        updateState(SolaxBindingConstants.CHANNEL_RAW_DATA, new StringType(inverterData.getRawData()));
+
+        Set<String> supportedChannels = parser.getSupportedChannels();
+        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_PV1_POWER, inverterData.getPV1Power(), Units.WATT,
+                supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_PV1_CURRENT, inverterData.getPV1Current(), Units.AMPERE,
+                supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_PV1_VOLTAGE, inverterData.getPV1Voltage(), Units.VOLT,
+                supportedChannels);
+
+        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_PV2_POWER, inverterData.getPV2Power(), Units.WATT,
+                supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_PV2_CURRENT, inverterData.getPV2Current(), Units.AMPERE,
+                supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_PV2_VOLTAGE, inverterData.getPV2Voltage(), Units.VOLT,
+                supportedChannels);
+
+        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_PV_TOTAL_POWER, inverterData.getPVTotalPower(), Units.WATT,
+                supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_PV_TOTAL_CURRENT, inverterData.getPVTotalCurrent(),
+                Units.AMPERE, supportedChannels);
+
+        updateChannel(SolaxBindingConstants.CHANNEL_BATTERY_POWER, inverterData.getBatteryPower(), Units.WATT,
+                supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_BATTERY_CURRENT, inverterData.getBatteryCurrent(), Units.AMPERE,
+                supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_BATTERY_VOLTAGE, inverterData.getBatteryVoltage(), Units.VOLT,
+                supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_BATTERY_TEMPERATURE, inverterData.getBatteryTemperature(),
+                SIUnits.CELSIUS, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_BATTERY_STATE_OF_CHARGE, inverterData.getBatteryLevel(),
+                Units.PERCENT, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_FEED_IN_POWER, inverterData.getFeedInPower(), Units.WATT,
+                supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_POWER_USAGE, inverterData.getPowerUsage(), Units.WATT,
+                supportedChannels);
+        updateState(SolaxBindingConstants.CHANNEL_INVERTER_WORKMODE,
+                new StringType(inverterData.getInverterWorkMode()));
+
+        // Totals
+        updateChannel(SolaxBindingConstants.CHANNEL_TOTAL_ENERGY, inverterData.getTotalEnergy(), Units.KILOWATT_HOUR,
+                supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_TOTAL_BATTERY_DISCHARGE_ENERGY,
+                inverterData.getTotalBatteryDischargeEnergy(), Units.KILOWATT_HOUR, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_TOTAL_BATTERY_CHARGE_ENERGY,
+                inverterData.getTotalBatteryChargeEnergy(), Units.KILOWATT_HOUR, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_TOTAL_PV_ENERGY, inverterData.getTotalPVEnergy(),
+                Units.KILOWATT_HOUR, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_TOTAL_FEED_IN_ENERGY, inverterData.getTotalFeedInEnergy(),
+                Units.KILOWATT_HOUR, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_TOTAL_CONSUMPTION, inverterData.getTotalConsumption(),
+                Units.KILOWATT_HOUR, supportedChannels);
+
+        // Today's
+        updateChannel(SolaxBindingConstants.CHANNEL_TODAY_ENERGY, inverterData.getTodayEnergy(), Units.KILOWATT_HOUR,
+                supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_TODAY_BATTERY_DISCHARGE_ENERGY,
+                inverterData.getTodayBatteryDischargeEnergy(), Units.KILOWATT_HOUR, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_TODAY_BATTERY_CHARGE_ENERGY,
+                inverterData.getTodayBatteryChargeEnergy(), Units.KILOWATT_HOUR, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_TODAY_FEED_IN_ENERGY, inverterData.getTodayFeedInEnergy(),
+                Units.KILOWATT_HOUR, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_TODAY_CONSUMPTION, inverterData.getTodayConsumption(),
+                Units.KILOWATT_HOUR, supportedChannels);
+
+        // Single phase specific channels
+        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_POWER, inverterData.getInverterOutputPower(),
+                Units.WATT, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_CURRENT, inverterData.getInverterCurrent(),
+                Units.AMPERE, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_VOLTAGE, inverterData.getInverterVoltage(),
+                Units.VOLT, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_FREQUENCY, inverterData.getInverterFrequency(),
+                Units.HERTZ, supportedChannels);
+
+        // Three phase specific channels
+        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_POWER_PHASE1, inverterData.getOutputPowerPhase1(),
+                Units.WATT, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_POWER_PHASE2, inverterData.getOutputPowerPhase2(),
+                Units.WATT, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_POWER_PHASE3, inverterData.getOutputPowerPhase3(),
+                Units.WATT, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_TOTAL_OUTPUT_POWER, inverterData.getTotalOutputPower(),
+                Units.WATT, supportedChannels);
+
+        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_CURRENT_PHASE1, inverterData.getCurrentPhase1(),
+                Units.AMPERE, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_CURRENT_PHASE2, inverterData.getCurrentPhase2(),
+                Units.AMPERE, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_CURRENT_PHASE3, inverterData.getCurrentPhase3(),
+                Units.AMPERE, supportedChannels);
+
+        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_VOLTAGE_PHASE1, inverterData.getVoltagePhase1(),
+                Units.VOLT, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_VOLTAGE_PHASE2, inverterData.getVoltagePhase2(),
+                Units.VOLT, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_VOLTAGE_PHASE3, inverterData.getVoltagePhase3(),
+                Units.VOLT, supportedChannels);
+
+        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_FREQUENCY_PHASE1, inverterData.getFrequencyPhase1(),
+                Units.HERTZ, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_FREQUENCY_PHASE2, inverterData.getFrequencyPhase2(),
+                Units.HERTZ, supportedChannels);
+        updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_FREQUENCY_PHASE3, inverterData.getFrequencyPhase3(),
+                Units.HERTZ, supportedChannels);
+
+        // Binding provided data
+        updateState(SolaxBindingConstants.CHANNEL_TIMESTAMP, new DateTimeType(ZonedDateTime.now()));
+    }
+}
index 17a88adecbbab6f911c2ae67c95e47c8358a899b..09996a0759833a54458e6e4406a28d71d85558bd 100644 (file)
@@ -19,11 +19,11 @@ 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;
+import org.openhab.binding.solax.internal.model.local.RawDataParser;
+import org.openhab.binding.solax.internal.model.local.inverters.X1BoostAirMiniDataParser;
+import org.openhab.binding.solax.internal.model.local.inverters.X1HybridG4DataParser;
+import org.openhab.binding.solax.internal.model.local.inverters.X3HybridG4DataParser;
+import org.openhab.binding.solax.internal.model.local.inverters.X3MicOrProG2DataParser;
 
 /**
  * The {@link InverterType} class is enum representing the different inverter types with a simple logic to convert from
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/CommonLocalDeviceData.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/CommonLocalDeviceData.java
new file mode 100644 (file)
index 0000000..4a6b169
--- /dev/null
@@ -0,0 +1,85 @@
+/**
+ * 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.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean;
+import org.openhab.binding.solax.internal.model.InverterType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link CommonLocalDeviceData} is an abstract class that contains the common information, applicable for all
+ * devices.
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public abstract class CommonLocalDeviceData implements LocalData {
+
+    private final Logger logger = LoggerFactory.getLogger(CommonLocalDeviceData.class);
+
+    private LocalConnectRawDataBean data;
+
+    public CommonLocalDeviceData(LocalConnectRawDataBean data) {
+        this.data = data;
+    }
+
+    @Override
+    public @Nullable String getRawData() {
+        return data.getRawData();
+    }
+
+    @Override
+    public @Nullable String getWifiSerial() {
+        return data.getSn();
+    }
+
+    @Override
+    public @Nullable String getWifiVersion() {
+        return data.getVer();
+    }
+
+    @Override
+    public InverterType getInverterType() {
+        return InverterType.fromIndex(data.getType());
+    }
+
+    protected Short getFromRawData(int index) {
+        try {
+            short[] dataArray = data.getData();
+            if (dataArray != null) {
+                return dataArray[index];
+            }
+        } catch (IndexOutOfBoundsException e) {
+            logger.debug("Tried to get data out of bounds of the raw data array.", e);
+        }
+        return 0;
+    }
+
+    public long packU16(int indexMajor, int indexMinor) {
+        short major = getFromRawData(indexMajor);
+        short minor = getFromRawData(indexMinor);
+        if (major == 0) {
+            return minor;
+        }
+
+        return Integer.toUnsignedLong(major << 16 | minor & 0xFFFF);
+    }
+
+    @Override
+    public LocalConnectRawDataBean getData() {
+        return data;
+    }
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/CommonLocalInverterData.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/CommonLocalInverterData.java
deleted file mode 100644 (file)
index 6d7f23f..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/**
- * 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.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean;
-import org.openhab.binding.solax.internal.model.InverterType;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link CommonLocalInverterData} is an abstract class that contains the common information, applicable for all
- * inverters.
- *
- * @author Konstantin Polihronov - Initial contribution
- */
-@NonNullByDefault
-public abstract class CommonLocalInverterData implements LocalInverterData {
-
-    private final Logger logger = LoggerFactory.getLogger(CommonLocalInverterData.class);
-
-    private LocalConnectRawDataBean data;
-
-    public CommonLocalInverterData(LocalConnectRawDataBean data) {
-        this.data = data;
-    }
-
-    @Override
-    public @Nullable String getRawData() {
-        return data.getRawData();
-    }
-
-    @Override
-    public @Nullable String getWifiSerial() {
-        return data.getSn();
-    }
-
-    @Override
-    public @Nullable String getWifiVersion() {
-        return data.getVer();
-    }
-
-    @Override
-    public InverterType getInverterType() {
-        return InverterType.fromIndex(data.getType());
-    }
-
-    protected short getData(int index) {
-        try {
-            short[] dataArray = data.getData();
-            if (dataArray != null) {
-                return dataArray[index];
-            }
-        } catch (IndexOutOfBoundsException e) {
-            logger.debug("Tried to get data out of bounds of the raw data array.", e);
-        }
-        return 0;
-    }
-
-    public long packU16(int indexMajor, int indexMinor) {
-        short major = getData(indexMajor);
-        short minor = getData(indexMinor);
-        if (major == 0) {
-            return minor;
-        }
-
-        return Integer.toUnsignedLong(major << 16 | minor & 0xFFFF);
-    }
-}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/EvChargerData.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/EvChargerData.java
new file mode 100644 (file)
index 0000000..41962e6
--- /dev/null
@@ -0,0 +1,161 @@
+/**
+ * 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;
+import org.openhab.binding.solax.internal.model.InverterType;
+import org.openhab.binding.solax.internal.util.ByteUtil;
+
+/**
+ * The {@link EvChargerData} is the data representation of the EV charger, retrieved from the raw data array.
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public class EvChargerData extends CommonLocalDeviceData {
+
+    public EvChargerData(LocalConnectRawDataBean bean) {
+        super(bean);
+    }
+
+    @Override
+    public InverterType getInverterType() {
+        return InverterType.UNKNOWN;
+    }
+
+    public String getDeviceState() {
+        return String.valueOf(getFromRawData(0));
+    }
+
+    public String getDeviceMode() {
+        return String.valueOf(getFromRawData(1));
+    }
+
+    public double getEqSingle() {
+        return getFromRawData(12).doubleValue() / 10;
+    }
+
+    public double getEqTotal() {
+        return ((double) ByteUtil.read32BitSigned(getFromRawData(14), getFromRawData(15))) / 10;
+    }
+
+    public short getTotalChargePower() {
+        return getFromRawData(11);
+    }
+
+    @Override
+    public double getVoltagePhase1() {
+        return getFromRawData(2).doubleValue() / 100;
+    }
+
+    @Override
+    public double getVoltagePhase2() {
+        return getFromRawData(3).doubleValue() / 100;
+    }
+
+    @Override
+    public double getVoltagePhase3() {
+        return getFromRawData(4).doubleValue() / 100;
+    }
+
+    @Override
+    public double getCurrentPhase1() {
+        return getFromRawData(5).doubleValue() / 100;
+    }
+
+    @Override
+    public double getCurrentPhase2() {
+        return getFromRawData(6).doubleValue() / 100;
+    }
+
+    @Override
+    public double getCurrentPhase3() {
+        return getFromRawData(7).doubleValue() / 100;
+    }
+
+    @Override
+    public short getOutputPowerPhase1() {
+        return getFromRawData(8);
+    }
+
+    @Override
+    public short getOutputPowerPhase2() {
+        return getFromRawData(9);
+    }
+
+    @Override
+    public short getOutputPowerPhase3() {
+        return getFromRawData(10);
+    }
+
+    public double getExternalCurrentPhase1() {
+        return getFromRawData(16).doubleValue() / 100;
+    }
+
+    public double getExternalCurrentPhase2() {
+        return getFromRawData(17).doubleValue() / 100;
+    }
+
+    public double getExternalCurrentPhase3() {
+        return getFromRawData(18).doubleValue() / 100;
+    }
+
+    public double getExternalPowerPhase1() {
+        return getFromRawData(19).intValue();
+    }
+
+    public double getExternalPowerPhase2() {
+        return getFromRawData(20).intValue();
+    }
+
+    public double getExternalPowerPhase3() {
+        return getFromRawData(21).intValue();
+    }
+
+    public double getExternalTotalPower() {
+        return getFromRawData(22).intValue();
+    }
+
+    public short getPlugTemperature() {
+        return getFromRawData(23);
+    }
+
+    public short getInternalTemperature() {
+        return getFromRawData(24);
+    }
+
+    public short getCPState() {
+        return getFromRawData(26);
+    }
+
+    public int getChargingDuration() {
+        return ByteUtil.read32BitSigned(getFromRawData(80), getFromRawData(81));
+    }
+
+    public short getOccpOfflineMode() {
+        return getFromRawData(85);
+    }
+
+    public short getTypePower() {
+        return getFromRawData(87);
+    }
+
+    public short getTypePhase() {
+        return getFromRawData(88);
+    }
+
+    public short getTypeCharger() {
+        return getFromRawData(89);
+    }
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/LocalData.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/LocalData.java
new file mode 100644 (file)
index 0000000..f9c6ac7
--- /dev/null
@@ -0,0 +1,241 @@
+/**
+ * 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.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean;
+import org.openhab.binding.solax.internal.model.InverterType;
+
+/**
+ * The {@link LocalData} Interface for the parsed inverter data in meaningful format
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public interface LocalData {
+
+    @Nullable
+    default String getWifiSerial() {
+        return getData().getSn();
+    }
+
+    @Nullable
+    default String getWifiVersion() {
+        return getData().getVer();
+    }
+
+    InverterType getInverterType();
+
+    @Nullable
+    default String getRawData() {
+        return getData().getRawData();
+    }
+
+    LocalConnectRawDataBean getData();
+
+    default double getPV1Voltage() {
+        return Short.MIN_VALUE;
+    }
+
+    default double getPV1Current() {
+        return Short.MIN_VALUE;
+    }
+
+    default short getPV1Power() {
+        return Short.MIN_VALUE;
+    }
+
+    default double getPV2Voltage() {
+        return Short.MIN_VALUE;
+    }
+
+    default double getPV2Current() {
+        return Short.MIN_VALUE;
+    }
+
+    default short getPV2Power() {
+        return Short.MIN_VALUE;
+    }
+
+    default double getPVTotalPower() {
+        return getPV1Power() + getPV2Power();
+    }
+
+    default double getPVTotalCurrent() {
+        return getPV1Current() + getPV2Current();
+    }
+
+    default double getBatteryVoltage() {
+        return Short.MIN_VALUE;
+    }
+
+    default double getBatteryCurrent() {
+        return Short.MIN_VALUE;
+    }
+
+    default short getBatteryPower() {
+        return Short.MIN_VALUE;
+    }
+
+    default short getBatteryTemperature() {
+        return Short.MIN_VALUE;
+    }
+
+    default short getInverterTemperature1() {
+        return Short.MIN_VALUE;
+    }
+
+    default short getInverterTemperature2() {
+        return Short.MIN_VALUE;
+    }
+
+    default short getBatteryLevel() {
+        return Short.MIN_VALUE;
+    }
+
+    default short getFeedInPower() {
+        return Short.MIN_VALUE;
+    }
+
+    default short getPowerUsage() {
+        return Short.MIN_VALUE;
+    }
+
+    default double getTotalEnergy() {
+        return Short.MIN_VALUE;
+    }
+
+    default short getTotalBatteryDischargeEnergy() {
+        return Short.MIN_VALUE;
+    }
+
+    default short getTotalBatteryChargeEnergy() {
+        return Short.MIN_VALUE;
+    }
+
+    default double getTotalPVEnergy() {
+        return Short.MIN_VALUE;
+    }
+
+    default short getTotalFeedInEnergy() {
+        return Short.MIN_VALUE;
+    }
+
+    default double getTotalConsumption() {
+        return Short.MIN_VALUE;
+    }
+
+    default double getTodayEnergy() {
+        return Short.MIN_VALUE;
+    }
+
+    default double getTodayFeedInEnergy() {
+        return Short.MIN_VALUE;
+    }
+
+    default double getTodayConsumption() {
+        return Short.MIN_VALUE;
+    }
+
+    default double getTodayBatteryDischargeEnergy() {
+        return Short.MIN_VALUE;
+    }
+
+    default double getTodayBatteryChargeEnergy() {
+        return Short.MIN_VALUE;
+    }
+
+    default double getInverterVoltage() {
+        return Short.MIN_VALUE;
+    }
+
+    default double getInverterCurrent() {
+        return Short.MIN_VALUE;
+    }
+
+    default short getInverterOutputPower() {
+        return Short.MIN_VALUE;
+    }
+
+    default double getInverterFrequency() {
+        return Short.MIN_VALUE;
+    }
+
+    default double getVoltagePhase1() {
+        return Short.MIN_VALUE;
+    }
+
+    default double getVoltagePhase2() {
+        return Short.MIN_VALUE;
+    }
+
+    default double getVoltagePhase3() {
+        return Short.MIN_VALUE;
+    }
+
+    default double getCurrentPhase1() {
+        return Short.MIN_VALUE;
+    }
+
+    default double getCurrentPhase2() {
+        return Short.MIN_VALUE;
+    }
+
+    default double getCurrentPhase3() {
+        return Short.MIN_VALUE;
+    }
+
+    default short getOutputPowerPhase1() {
+        return Short.MIN_VALUE;
+    }
+
+    default short getOutputPowerPhase2() {
+        return Short.MIN_VALUE;
+    }
+
+    default short getOutputPowerPhase3() {
+        return Short.MIN_VALUE;
+    }
+
+    default short getTotalOutputPower() {
+        return Short.MIN_VALUE;
+    }
+
+    default double getFrequencyPhase1() {
+        return Short.MIN_VALUE;
+    }
+
+    default double getFrequencyPhase2() {
+        return Short.MIN_VALUE;
+    }
+
+    default double getFrequencyPhase3() {
+        return Short.MIN_VALUE;
+    }
+
+    default short getInverterWorkModeCode() {
+        return Short.MIN_VALUE;
+    }
+
+    default String getInverterWorkMode() {
+        return String.valueOf(getInverterWorkModeCode());
+    }
+
+    default String toStringDetailed() {
+        return "WifiSerial = " + getWifiSerial() + ", WifiVersion = " + getWifiVersion() + ", InverterType = "
+                + getInverterType() + ", BatteryPower = " + getBatteryPower() + "W, Battery SoC = " + getBatteryLevel()
+                + "%, FeedIn Power = " + getFeedInPower() + "W, Total PV Power = " + (getPV1Power() + getPV2Power())
+                + "W";
+    }
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/LocalInverterData.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/LocalInverterData.java
deleted file mode 100644 (file)
index 106aaee..0000000
+++ /dev/null
@@ -1,232 +0,0 @@
-/**
- * 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.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.solax.internal.model.InverterType;
-
-/**
- * The {@link LocalInverterData} Interface for the parsed inverter data in meaningful format
- *
- * @author Konstantin Polihronov - Initial contribution
- */
-@NonNullByDefault
-public interface LocalInverterData {
-
-    @Nullable
-    String getWifiSerial();
-
-    @Nullable
-    String getWifiVersion();
-
-    InverterType getInverterType();
-
-    @Nullable
-    String getRawData();
-
-    default double getPV1Voltage() {
-        return Short.MIN_VALUE;
-    }
-
-    default double getPV1Current() {
-        return Short.MIN_VALUE;
-    }
-
-    default short getPV1Power() {
-        return Short.MIN_VALUE;
-    }
-
-    default double getPV2Voltage() {
-        return Short.MIN_VALUE;
-    }
-
-    default double getPV2Current() {
-        return Short.MIN_VALUE;
-    }
-
-    default short getPV2Power() {
-        return Short.MIN_VALUE;
-    }
-
-    default double getPVTotalPower() {
-        return getPV1Power() + getPV2Power();
-    }
-
-    default double getPVTotalCurrent() {
-        return getPV1Current() + getPV2Current();
-    }
-
-    default double getBatteryVoltage() {
-        return Short.MIN_VALUE;
-    }
-
-    default double getBatteryCurrent() {
-        return Short.MIN_VALUE;
-    }
-
-    default short getBatteryPower() {
-        return Short.MIN_VALUE;
-    }
-
-    default short getBatteryTemperature() {
-        return Short.MIN_VALUE;
-    }
-
-    default short getInverterTemperature1() {
-        return Short.MIN_VALUE;
-    }
-
-    default short getInverterTemperature2() {
-        return Short.MIN_VALUE;
-    }
-
-    default short getBatteryLevel() {
-        return Short.MIN_VALUE;
-    }
-
-    default short getFeedInPower() {
-        return Short.MIN_VALUE;
-    }
-
-    default short getPowerUsage() {
-        return Short.MIN_VALUE;
-    }
-
-    default double getTotalEnergy() {
-        return Short.MIN_VALUE;
-    }
-
-    default short getTotalBatteryDischargeEnergy() {
-        return Short.MIN_VALUE;
-    }
-
-    default short getTotalBatteryChargeEnergy() {
-        return Short.MIN_VALUE;
-    }
-
-    default double getTotalPVEnergy() {
-        return Short.MIN_VALUE;
-    }
-
-    default short getTotalFeedInEnergy() {
-        return Short.MIN_VALUE;
-    }
-
-    default double getTotalConsumption() {
-        return Short.MIN_VALUE;
-    }
-
-    default double getTodayEnergy() {
-        return Short.MIN_VALUE;
-    }
-
-    default double getTodayFeedInEnergy() {
-        return Short.MIN_VALUE;
-    }
-
-    default double getTodayConsumption() {
-        return Short.MIN_VALUE;
-    }
-
-    default double getTodayBatteryDischargeEnergy() {
-        return Short.MIN_VALUE;
-    }
-
-    default double getTodayBatteryChargeEnergy() {
-        return Short.MIN_VALUE;
-    }
-
-    default double getInverterVoltage() {
-        return Short.MIN_VALUE;
-    }
-
-    default double getInverterCurrent() {
-        return Short.MIN_VALUE;
-    }
-
-    default short getInverterOutputPower() {
-        return Short.MIN_VALUE;
-    }
-
-    default double getInverterFrequency() {
-        return Short.MIN_VALUE;
-    }
-
-    default double getVoltagePhase1() {
-        return Short.MIN_VALUE;
-    }
-
-    default double getVoltagePhase2() {
-        return Short.MIN_VALUE;
-    }
-
-    default double getVoltagePhase3() {
-        return Short.MIN_VALUE;
-    }
-
-    default double getCurrentPhase1() {
-        return Short.MIN_VALUE;
-    }
-
-    default double getCurrentPhase2() {
-        return Short.MIN_VALUE;
-    }
-
-    default double getCurrentPhase3() {
-        return Short.MIN_VALUE;
-    }
-
-    default short getOutputPowerPhase1() {
-        return Short.MIN_VALUE;
-    }
-
-    default short getOutputPowerPhase2() {
-        return Short.MIN_VALUE;
-    }
-
-    default short getOutputPowerPhase3() {
-        return Short.MIN_VALUE;
-    }
-
-    default short getTotalOutputPower() {
-        return Short.MIN_VALUE;
-    }
-
-    default double getFrequencyPhase1() {
-        return Short.MIN_VALUE;
-    }
-
-    default double getFrequencyPhase2() {
-        return Short.MIN_VALUE;
-    }
-
-    default double getFrequencyPhase3() {
-        return Short.MIN_VALUE;
-    }
-
-    default short getInverterWorkModeCode() {
-        return Short.MIN_VALUE;
-    }
-
-    default String getInverterWorkMode() {
-        return String.valueOf(getInverterWorkModeCode());
-    }
-
-    default String toStringDetailed() {
-        return "WifiSerial = " + getWifiSerial() + ", WifiVersion = " + getWifiVersion() + ", InverterType = "
-                + getInverterType() + ", BatteryPower = " + getBatteryPower() + "W, Battery SoC = " + getBatteryLevel()
-                + "%, FeedIn Power = " + getFeedInPower() + "W, Total PV Power = " + (getPV1Power() + getPV2Power())
-                + "W";
-    }
-}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/RawDataParser.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/RawDataParser.java
new file mode 100644 (file)
index 0000000..7214d30
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * 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 java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean;
+
+/**
+ * The {@link RawDataParser} declares generic parser implementation that parses raw data to generic inverter data which
+ * is common for all inverters.
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public interface RawDataParser {
+
+    LocalData getData(LocalConnectRawDataBean bean);
+
+    Set<String> getSupportedChannels();
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X1BoostAirMiniData.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X1BoostAirMiniData.java
new file mode 100644 (file)
index 0000000..104ac87
--- /dev/null
@@ -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 X1BoostAirMiniData} 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 X1BoostAirMiniData extends CommonLocalDeviceData {
+
+    public X1BoostAirMiniData(LocalConnectRawDataBean data) {
+        super(data);
+    }
+
+    @Override
+    public double getInverterVoltage() {
+        return (double) getFromRawData(0) / 10;
+    }
+
+    @Override
+    public double getInverterCurrent() {
+        return (double) getFromRawData(1) / 10;
+    }
+
+    @Override
+    public short getInverterOutputPower() {
+        return getFromRawData(2);
+    }
+
+    @Override
+    public double getPV1Voltage() {
+        return (double) getFromRawData(3) / 10;
+    }
+
+    @Override
+    public double getPV2Voltage() {
+        return (double) getFromRawData(4) / 10;
+    }
+
+    @Override
+    public double getPV1Current() {
+        return (double) getFromRawData(5) / 10;
+    }
+
+    @Override
+    public double getPV2Current() {
+        return (double) getFromRawData(6) / 10;
+    }
+
+    @Override
+    public short getPV1Power() {
+        return getFromRawData(7);
+    }
+
+    @Override
+    public short getPV2Power() {
+        return getFromRawData(8);
+    }
+
+    @Override
+    public double getInverterFrequency() {
+        return (double) getFromRawData(9) / 100;
+    }
+
+    @Override
+    public double getTotalEnergy() {
+        return (double) getFromRawData(11) / 10;
+    }
+
+    @Override
+    public double getTodayEnergy() {
+        return (double) getFromRawData(13) / 10;
+    }
+
+    @Override
+    public short getPowerUsage() {
+        return (short) Math.round((double) getFromRawData(43) / 10);
+    }
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X1BoostAirMiniInverterData.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X1BoostAirMiniInverterData.java
deleted file mode 100644 (file)
index 9c4e951..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/**
- * 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);
-    }
-}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X1HybridG4Data.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X1HybridG4Data.java
new file mode 100644 (file)
index 0000000..ea59e4b
--- /dev/null
@@ -0,0 +1,115 @@
+/**
+ * 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 X1HybridG4Data} is an implementation of the single phased inverter data interface for X1 Hybrid G4
+ * inverter.
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public class X1HybridG4Data extends CommonLocalDeviceData {
+
+    public X1HybridG4Data(LocalConnectRawDataBean data) {
+        super(data);
+    }
+
+    @Override
+    public double getInverterVoltage() {
+        return ((double) getFromRawData(0)) / 10;
+    }
+
+    @Override
+    public double getInverterCurrent() {
+        return ((double) getFromRawData(1)) / 10;
+    }
+
+    @Override
+    public short getInverterOutputPower() {
+        return getFromRawData(2);
+    }
+
+    @Override
+    public double getInverterFrequency() {
+        return ((double) getFromRawData(3)) / 100;
+    }
+
+    @Override
+    public short getFeedInPower() {
+        return getFromRawData(32);
+    }
+
+    @Override
+    public double getPV1Voltage() {
+        return ((double) getFromRawData(4)) / 10;
+    }
+
+    @Override
+    public double getPV2Voltage() {
+        return ((double) getFromRawData(5)) / 10;
+    }
+
+    @Override
+    public double getPV1Current() {
+        return ((double) getFromRawData(6)) / 10;
+    }
+
+    @Override
+    public double getPV2Current() {
+        return ((double) getFromRawData(7)) / 10;
+    }
+
+    @Override
+    public short getPV1Power() {
+        return getFromRawData(8);
+    }
+
+    @Override
+    public short getPV2Power() {
+        return getFromRawData(9);
+    }
+
+    @Override
+    public double getBatteryVoltage() {
+        return ((double) getFromRawData(14)) / 100;
+    }
+
+    @Override
+    public double getBatteryCurrent() {
+        return ((double) getFromRawData(15)) / 100;
+    }
+
+    @Override
+    public short getBatteryPower() {
+        return getFromRawData(16);
+    }
+
+    @Override
+    public short getBatteryTemperature() {
+        return getFromRawData(17);
+    }
+
+    @Override
+    public short getBatteryLevel() {
+        return getFromRawData(18);
+    }
+
+    @Override
+    public short getInverterWorkModeCode() {
+        return getFromRawData(10);
+    }
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X1HybridG4InverterData.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X1HybridG4InverterData.java
deleted file mode 100644 (file)
index b536459..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/**
- * 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 X1HybridG4InverterData} is an implementation of the single phased inverter data interface for X1 Hybrid G4
- * inverter.
- *
- * @author Konstantin Polihronov - Initial contribution
- */
-@NonNullByDefault
-public class X1HybridG4InverterData extends CommonLocalInverterData {
-
-    public X1HybridG4InverterData(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 getInverterFrequency() {
-        return ((double) getData(3)) / 100;
-    }
-
-    @Override
-    public short getFeedInPower() {
-        return getData(32);
-    }
-
-    @Override
-    public double getPV1Voltage() {
-        return ((double) getData(4)) / 10;
-    }
-
-    @Override
-    public double getPV2Voltage() {
-        return ((double) getData(5)) / 10;
-    }
-
-    @Override
-    public double getPV1Current() {
-        return ((double) getData(6)) / 10;
-    }
-
-    @Override
-    public double getPV2Current() {
-        return ((double) getData(7)) / 10;
-    }
-
-    @Override
-    public short getPV1Power() {
-        return getData(8);
-    }
-
-    @Override
-    public short getPV2Power() {
-        return getData(9);
-    }
-
-    @Override
-    public double getBatteryVoltage() {
-        return ((double) getData(14)) / 100;
-    }
-
-    @Override
-    public double getBatteryCurrent() {
-        return ((double) getData(15)) / 100;
-    }
-
-    @Override
-    public short getBatteryPower() {
-        return getData(16);
-    }
-
-    @Override
-    public short getBatteryTemperature() {
-        return getData(17);
-    }
-
-    @Override
-    public short getBatteryLevel() {
-        return getData(18);
-    }
-
-    @Override
-    public short getInverterWorkModeCode() {
-        return getData(10);
-    }
-}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X3HybridG4Data.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X3HybridG4Data.java
new file mode 100644 (file)
index 0000000..a1f1b16
--- /dev/null
@@ -0,0 +1,228 @@
+/**
+ * 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 X3HybridG4Data} is an implementation of the single phased inverter data interface for X3 Hybrid G4
+ * inverter.
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public class X3HybridG4Data extends CommonLocalDeviceData {
+
+    public X3HybridG4Data(LocalConnectRawDataBean data) {
+        super(data);
+    }
+
+    // Inverter data
+
+    @Override
+    public double getVoltagePhase1() {
+        return ((double) getFromRawData(0)) / 10;
+    }
+
+    @Override
+    public double getVoltagePhase2() {
+        return ((double) getFromRawData(1)) / 10;
+    }
+
+    @Override
+    public double getVoltagePhase3() {
+        return ((double) getFromRawData(2)) / 10;
+    }
+
+    @Override
+    public double getCurrentPhase1() {
+        return ((double) getFromRawData(3)) / 10;
+    }
+
+    @Override
+    public double getCurrentPhase2() {
+        return ((double) getFromRawData(4)) / 10;
+    }
+
+    @Override
+    public double getCurrentPhase3() {
+        return ((double) getFromRawData(5)) / 10;
+    }
+
+    @Override
+    public short getOutputPowerPhase1() {
+        return getFromRawData(6);
+    }
+
+    @Override
+    public short getOutputPowerPhase2() {
+        return getFromRawData(7);
+    }
+
+    @Override
+    public short getOutputPowerPhase3() {
+        return getFromRawData(8);
+    }
+
+    @Override
+    public short getTotalOutputPower() {
+        return getFromRawData(9);
+    }
+
+    @Override
+    public double getPV1Voltage() {
+        return ((double) getFromRawData(10)) / 10;
+    }
+
+    @Override
+    public double getPV2Voltage() {
+        return ((double) getFromRawData(11)) / 10;
+    }
+
+    @Override
+    public double getPV1Current() {
+        return ((double) getFromRawData(12)) / 10;
+    }
+
+    @Override
+    public double getPV2Current() {
+        return ((double) getFromRawData(13)) / 10;
+    }
+
+    @Override
+    public short getPV1Power() {
+        return getFromRawData(14);
+    }
+
+    @Override
+    public short getPV2Power() {
+        return getFromRawData(15);
+    }
+
+    @Override
+    public double getFrequencyPhase1() {
+        return ((double) getFromRawData(16)) / 100;
+    }
+
+    @Override
+    public double getFrequencyPhase2() {
+        return ((double) getFromRawData(17)) / 100;
+    }
+
+    @Override
+    public double getFrequencyPhase3() {
+        return ((double) getFromRawData(18)) / 100;
+    }
+
+    // Battery
+
+    @Override
+    public double getBatteryVoltage() {
+        return ((double) getFromRawData(39)) / 100;
+    }
+
+    @Override
+    public double getBatteryCurrent() {
+        return ((double) getFromRawData(40)) / 100;
+    }
+
+    @Override
+    public short getBatteryPower() {
+        return getFromRawData(41);
+    }
+
+    @Override
+    public short getBatteryTemperature() {
+        return getFromRawData(105);
+    }
+
+    @Override
+    public short getBatteryLevel() {
+        return getFromRawData(103);
+    }
+
+    // Feed in power
+
+    @Override
+    public short getFeedInPower() {
+        return (short) (getFromRawData(34) - getFromRawData(35));
+    }
+
+    // Totals
+
+    @Override
+    public short getPowerUsage() {
+        return getFromRawData(47);
+    }
+
+    @Override
+    public double getTotalEnergy() {
+        return ((double) getFromRawData(68)) / 10;
+    }
+
+    @Override
+    public short getTotalBatteryDischargeEnergy() {
+        return getFromRawData(74);
+    }
+
+    @Override
+    public short getTotalBatteryChargeEnergy() {
+        return getFromRawData(76);
+    }
+
+    @Override
+    public double getTotalPVEnergy() {
+        return ((double) getFromRawData(80)) / 10;
+    }
+
+    @Override
+    public short getTotalFeedInEnergy() {
+        return getFromRawData(86);
+    }
+
+    @Override
+    public double getTotalConsumption() {
+        return ((double) getFromRawData(88)) / 10;
+    }
+
+    @Override
+    public double getTodayEnergy() {
+        return ((double) getFromRawData(82)) / 10;
+    }
+
+    @Override
+    public double getTodayFeedInEnergy() {
+        return ((double) getFromRawData(90)) / 100;
+    }
+
+    @Override
+    public double getTodayConsumption() {
+        return ((double) getFromRawData(92)) / 100;
+    }
+
+    @Override
+    public double getTodayBatteryDischargeEnergy() {
+        return ((double) getFromRawData(78)) / 10;
+    }
+
+    @Override
+    public double getTodayBatteryChargeEnergy() {
+        return ((double) getFromRawData(79)) / 10;
+    }
+
+    @Override
+    public short getInverterWorkModeCode() {
+        return getFromRawData(19);
+    }
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X3HybridG4InverterData.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X3HybridG4InverterData.java
deleted file mode 100644 (file)
index b613fb4..0000000
+++ /dev/null
@@ -1,228 +0,0 @@
-/**
- * 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 X3HybridG4InverterData} is responsible for handling commands, which are
- * sent to one of the channels.
- *
- * @author Konstantin Polihronov - Initial contribution
- */
-@NonNullByDefault
-public class X3HybridG4InverterData extends CommonLocalInverterData {
-
-    public X3HybridG4InverterData(LocalConnectRawDataBean data) {
-        super(data);
-    }
-
-    // Inverter data
-
-    @Override
-    public double getVoltagePhase1() {
-        return ((double) getData(0)) / 10;
-    }
-
-    @Override
-    public double getVoltagePhase2() {
-        return ((double) getData(1)) / 10;
-    }
-
-    @Override
-    public double getVoltagePhase3() {
-        return ((double) getData(2)) / 10;
-    }
-
-    @Override
-    public double getCurrentPhase1() {
-        return ((double) getData(3)) / 10;
-    }
-
-    @Override
-    public double getCurrentPhase2() {
-        return ((double) getData(4)) / 10;
-    }
-
-    @Override
-    public double getCurrentPhase3() {
-        return ((double) getData(5)) / 10;
-    }
-
-    @Override
-    public short getOutputPowerPhase1() {
-        return getData(6);
-    }
-
-    @Override
-    public short getOutputPowerPhase2() {
-        return getData(7);
-    }
-
-    @Override
-    public short getOutputPowerPhase3() {
-        return getData(8);
-    }
-
-    @Override
-    public short getTotalOutputPower() {
-        return getData(9);
-    }
-
-    @Override
-    public double getPV1Voltage() {
-        return ((double) getData(10)) / 10;
-    }
-
-    @Override
-    public double getPV2Voltage() {
-        return ((double) getData(11)) / 10;
-    }
-
-    @Override
-    public double getPV1Current() {
-        return ((double) getData(12)) / 10;
-    }
-
-    @Override
-    public double getPV2Current() {
-        return ((double) getData(13)) / 10;
-    }
-
-    @Override
-    public short getPV1Power() {
-        return getData(14);
-    }
-
-    @Override
-    public short getPV2Power() {
-        return getData(15);
-    }
-
-    @Override
-    public double getFrequencyPhase1() {
-        return ((double) getData(16)) / 100;
-    }
-
-    @Override
-    public double getFrequencyPhase2() {
-        return ((double) getData(17)) / 100;
-    }
-
-    @Override
-    public double getFrequencyPhase3() {
-        return ((double) getData(18)) / 100;
-    }
-
-    // Battery
-
-    @Override
-    public double getBatteryVoltage() {
-        return ((double) getData(39)) / 100;
-    }
-
-    @Override
-    public double getBatteryCurrent() {
-        return ((double) getData(40)) / 100;
-    }
-
-    @Override
-    public short getBatteryPower() {
-        return getData(41);
-    }
-
-    @Override
-    public short getBatteryTemperature() {
-        return getData(105);
-    }
-
-    @Override
-    public short getBatteryLevel() {
-        return getData(103);
-    }
-
-    // Feed in power
-
-    @Override
-    public short getFeedInPower() {
-        return (short) (getData(34) - getData(35));
-    }
-
-    // Totals
-
-    @Override
-    public short getPowerUsage() {
-        return getData(47);
-    }
-
-    @Override
-    public double getTotalEnergy() {
-        return ((double) getData(68)) / 10;
-    }
-
-    @Override
-    public short getTotalBatteryDischargeEnergy() {
-        return getData(74);
-    }
-
-    @Override
-    public short getTotalBatteryChargeEnergy() {
-        return getData(76);
-    }
-
-    @Override
-    public double getTotalPVEnergy() {
-        return ((double) getData(80)) / 10;
-    }
-
-    @Override
-    public short getTotalFeedInEnergy() {
-        return getData(86);
-    }
-
-    @Override
-    public double getTotalConsumption() {
-        return ((double) getData(88)) / 10;
-    }
-
-    @Override
-    public double getTodayEnergy() {
-        return ((double) getData(82)) / 10;
-    }
-
-    @Override
-    public double getTodayFeedInEnergy() {
-        return ((double) getData(90)) / 100;
-    }
-
-    @Override
-    public double getTodayConsumption() {
-        return ((double) getData(92)) / 100;
-    }
-
-    @Override
-    public double getTodayBatteryDischargeEnergy() {
-        return ((double) getData(78)) / 10;
-    }
-
-    @Override
-    public double getTodayBatteryChargeEnergy() {
-        return ((double) getData(79)) / 10;
-    }
-
-    @Override
-    public short getInverterWorkModeCode() {
-        return getData(19);
-    }
-}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X3MicOrProG2Data.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X3MicOrProG2Data.java
new file mode 100644 (file)
index 0000000..b56fba9
--- /dev/null
@@ -0,0 +1,153 @@
+/**
+ * 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 X3HybridG4Data} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author Henrik Tóth - Initial contribution
+ *         (based on X1/X3 G4 parser from Konstantin Polihronov)
+ */
+@NonNullByDefault
+public class X3MicOrProG2Data extends CommonLocalDeviceData {
+
+    public X3MicOrProG2Data(LocalConnectRawDataBean data) {
+        super(data);
+    }
+
+    // Inverter data
+
+    @Override
+    public double getVoltagePhase1() {
+        return ((double) getFromRawData(0)) / 10;
+    }
+
+    @Override
+    public double getVoltagePhase2() {
+        return ((double) getFromRawData(1)) / 10;
+    }
+
+    @Override
+    public double getVoltagePhase3() {
+        return ((double) getFromRawData(2)) / 10;
+    }
+
+    @Override
+    public double getCurrentPhase1() {
+        return ((double) getFromRawData(3)) / 10;
+    }
+
+    @Override
+    public double getCurrentPhase2() {
+        return ((double) getFromRawData(4)) / 10;
+    }
+
+    @Override
+    public double getCurrentPhase3() {
+        return ((double) getFromRawData(5)) / 10;
+    }
+
+    @Override
+    public short getOutputPowerPhase1() {
+        return getFromRawData(6);
+    }
+
+    @Override
+    public short getOutputPowerPhase2() {
+        return getFromRawData(7);
+    }
+
+    @Override
+    public short getOutputPowerPhase3() {
+        return getFromRawData(8);
+    }
+
+    @Override
+    public double getPV1Voltage() {
+        return ((double) getFromRawData(9)) / 10;
+    }
+
+    @Override
+    public double getPV2Voltage() {
+        return ((double) getFromRawData(10)) / 10;
+    }
+
+    @Override
+    public double getPV1Current() {
+        return ((double) getFromRawData(12)) / 10;
+    }
+
+    @Override
+    public double getPV2Current() {
+        return ((double) getFromRawData(13)) / 10;
+    }
+
+    @Override
+    public short getPV1Power() {
+        return getFromRawData(15);
+    }
+
+    @Override
+    public short getPV2Power() {
+        return getFromRawData(16);
+    }
+
+    @Override
+    public double getFrequencyPhase1() {
+        return ((double) getFromRawData(18)) / 100;
+    }
+
+    @Override
+    public double getFrequencyPhase2() {
+        return ((double) getFromRawData(19)) / 100;
+    }
+
+    @Override
+    public double getFrequencyPhase3() {
+        return ((double) getFromRawData(20)) / 100;
+    }
+
+    @Override
+    public short getInverterWorkModeCode() {
+        return getFromRawData(21);
+    }
+
+    @Override
+    public double getTotalEnergy() {
+        return ((double) getFromRawData(22)) / 10;
+    }
+
+    @Override
+    public double getTodayEnergy() {
+        return ((double) getFromRawData(24)) / 10;
+    }
+
+    @Override
+    public short getInverterTemperature1() {
+        return getFromRawData(26);
+    }
+
+    @Override
+    public short getInverterTemperature2() {
+        return getFromRawData(27);
+    }
+
+    @Override
+    public short getTotalOutputPower() {
+        return getFromRawData(78);
+    }
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X3MicOrProG2InverterData.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/X3MicOrProG2InverterData.java
deleted file mode 100644 (file)
index bd0e03c..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-/**
- * 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 X3HybridG4InverterData} is responsible for handling commands, which are
- * sent to one of the channels.
- *
- * @author Henrik Tóth - Initial contribution
- *         (based on X1/X3 G4 parser from Konstantin Polihronov)
- */
-@NonNullByDefault
-public class X3MicOrProG2InverterData extends CommonLocalInverterData {
-
-    public X3MicOrProG2InverterData(LocalConnectRawDataBean data) {
-        super(data);
-    }
-
-    // Inverter data
-
-    @Override
-    public double getVoltagePhase1() {
-        return ((double) getData(0)) / 10;
-    }
-
-    @Override
-    public double getVoltagePhase2() {
-        return ((double) getData(1)) / 10;
-    }
-
-    @Override
-    public double getVoltagePhase3() {
-        return ((double) getData(2)) / 10;
-    }
-
-    @Override
-    public double getCurrentPhase1() {
-        return ((double) getData(3)) / 10;
-    }
-
-    @Override
-    public double getCurrentPhase2() {
-        return ((double) getData(4)) / 10;
-    }
-
-    @Override
-    public double getCurrentPhase3() {
-        return ((double) getData(5)) / 10;
-    }
-
-    @Override
-    public short getOutputPowerPhase1() {
-        return getData(6);
-    }
-
-    @Override
-    public short getOutputPowerPhase2() {
-        return getData(7);
-    }
-
-    @Override
-    public short getOutputPowerPhase3() {
-        return getData(8);
-    }
-
-    @Override
-    public double getPV1Voltage() {
-        return ((double) getData(9)) / 10;
-    }
-
-    @Override
-    public double getPV2Voltage() {
-        return ((double) getData(10)) / 10;
-    }
-
-    @Override
-    public double getPV1Current() {
-        return ((double) getData(12)) / 10;
-    }
-
-    @Override
-    public double getPV2Current() {
-        return ((double) getData(13)) / 10;
-    }
-
-    @Override
-    public short getPV1Power() {
-        return getData(15);
-    }
-
-    @Override
-    public short getPV2Power() {
-        return getData(16);
-    }
-
-    @Override
-    public double getFrequencyPhase1() {
-        return ((double) getData(18)) / 100;
-    }
-
-    @Override
-    public double getFrequencyPhase2() {
-        return ((double) getData(19)) / 100;
-    }
-
-    @Override
-    public double getFrequencyPhase3() {
-        return ((double) getData(20)) / 100;
-    }
-
-    @Override
-    public short getInverterWorkModeCode() {
-        return getData(21);
-    }
-
-    @Override
-    public double getTotalEnergy() {
-        return ((double) getData(22)) / 10;
-    }
-
-    @Override
-    public double getTodayEnergy() {
-        return ((double) getData(24)) / 10;
-    }
-
-    @Override
-    public short getInverterTemperature1() {
-        return getData(26);
-    }
-
-    @Override
-    public short getInverterTemperature2() {
-        return getData(27);
-    }
-
-    @Override
-    public short getTotalOutputPower() {
-        return getData(78);
-    }
-}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/evchargers/EvChargerDataParser.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/evchargers/EvChargerDataParser.java
new file mode 100644 (file)
index 0000000..bd08d99
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * 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.evchargers;
+
+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.EvChargerData;
+import org.openhab.binding.solax.internal.model.local.RawDataParser;
+
+/**
+ * The {@link EvChargerDataParser} is the implementation that parses raw data into a EvChargerData for the EV charger.
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public class EvChargerDataParser implements RawDataParser {
+
+    private static final Set<String> EV_CHARGER_SUPPORTED_CHANNELS = Set.of(CHANNEL_CHARGER_MODE, CHANNEL_CHARGER_STATE,
+            CHANNEL_CHARGER_EQ_SINGLE_SESSION, CHANNEL_CHARGER_EQ_TOTAL, CHANNEL_CHARGER_OUTPUT_POWER_PHASE1,
+            CHANNEL_CHARGER_OUTPUT_POWER_PHASE2, CHANNEL_CHARGER_OUTPUT_POWER_PHASE3,
+            CHANNEL_CHARGER_TOTAL_OUTPUT_POWER, CHANNEL_CHARGER_OUTPUT_CURRENT_PHASE1,
+            CHANNEL_CHARGER_OUTPUT_CURRENT_PHASE2, CHANNEL_CHARGER_OUTPUT_CURRENT_PHASE3,
+            CHANNEL_CHARGER_OUTPUT_VOLTAGE_PHASE1, CHANNEL_CHARGER_OUTPUT_VOLTAGE_PHASE2,
+            CHANNEL_CHARGER_OUTPUT_VOLTAGE_PHASE3, CHANNEL_CHARGER_EXTERNAL_CURRENT_PHASE1,
+            CHANNEL_CHARGER_EXTERNAL_CURRENT_PHASE2, CHANNEL_CHARGER_EXTERNAL_CURRENT_PHASE3,
+            CHANNEL_CHARGER_EXTERNAL_POWER_PHASE1, CHANNEL_CHARGER_EXTERNAL_POWER_PHASE2,
+            CHANNEL_CHARGER_EXTERNAL_POWER_PHASE3, CHANNEL_CHARGER_TOTAL_EXTERNAL_POWER,
+            CHANNEL_CHARGER_PLUG_TEMPERATURE, CHANNEL_CHARGER_INTERNAL_TEMPERATURE);
+
+    @Override
+    public EvChargerData getData(LocalConnectRawDataBean bean) {
+        return new EvChargerData(bean);
+    }
+
+    @Override
+    public Set<String> getSupportedChannels() {
+        return EV_CHARGER_SUPPORTED_CHANNELS;
+    }
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/inverters/X1BoostAirMiniDataParser.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/inverters/X1BoostAirMiniDataParser.java
new file mode 100644 (file)
index 0000000..d79bb23
--- /dev/null
@@ -0,0 +1,50 @@
+/**
+ * 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.inverters;
+
+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.LocalData;
+import org.openhab.binding.solax.internal.model.local.RawDataParser;
+import org.openhab.binding.solax.internal.model.local.X1BoostAirMiniData;
+
+/**
+ * The {@link X1BoostAirMiniDataParser} is the implementation that parses raw data into a {@link LocalData} 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 LocalData getData(LocalConnectRawDataBean bean) {
+        return new X1BoostAirMiniData(bean);
+    }
+
+    @Override
+    public Set<String> getSupportedChannels() {
+        return X1_BOOST_AIR_MINI_SUPPORTED_CHANNELS;
+    }
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/inverters/X1HybridG4DataParser.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/inverters/X1HybridG4DataParser.java
new file mode 100644 (file)
index 0000000..59abad6
--- /dev/null
@@ -0,0 +1,51 @@
+/**
+ * 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.inverters;
+
+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.LocalData;
+import org.openhab.binding.solax.internal.model.local.RawDataParser;
+import org.openhab.binding.solax.internal.model.local.X1HybridG4Data;
+
+/**
+ * The {@link X1HybridG4DataParser} is the implementation that parses raw data into a {@link LocalData} for the
+ * X1 Hybrid G4 inverter.
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public class X1HybridG4DataParser implements RawDataParser {
+
+    private static final Set<String> X1_HYBRID_G4_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_BATTERY_POWER, CHANNEL_BATTERY_VOLTAGE, CHANNEL_BATTERY_CURRENT,
+            CHANNEL_BATTERY_TEMPERATURE, CHANNEL_BATTERY_STATE_OF_CHARGE, CHANNEL_FEED_IN_POWER, CHANNEL_TIMESTAMP,
+            CHANNEL_RAW_DATA, CHANNEL_INVERTER_OUTPUT_POWER, CHANNEL_INVERTER_OUTPUT_CURRENT,
+            CHANNEL_INVERTER_OUTPUT_VOLTAGE, CHANNEL_INVERTER_OUTPUT_FREQUENCY, CHANNEL_INVERTER_WORKMODE);
+
+    @Override
+    public LocalData getData(LocalConnectRawDataBean rawData) {
+        return new X1HybridG4Data(rawData);
+    }
+
+    @Override
+    public Set<String> getSupportedChannels() {
+        return X1_HYBRID_G4_SUPPORTED_CHANNELS;
+    }
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/inverters/X3HybridG4DataParser.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/inverters/X3HybridG4DataParser.java
new file mode 100644 (file)
index 0000000..d023596
--- /dev/null
@@ -0,0 +1,59 @@
+/**
+ * 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.inverters;
+
+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.LocalData;
+import org.openhab.binding.solax.internal.model.local.RawDataParser;
+import org.openhab.binding.solax.internal.model.local.X3HybridG4Data;
+
+/**
+ * The {@link X3HybridG4DataParser} is the implementation that parses raw data into a {@link LocalData} for the
+ * X3 Hybrid G4 inverter.
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public class X3HybridG4DataParser implements RawDataParser {
+
+    private static final Set<String> X3_HYBRID_G4_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_BATTERY_POWER, CHANNEL_BATTERY_VOLTAGE, CHANNEL_BATTERY_CURRENT,
+            CHANNEL_BATTERY_TEMPERATURE, CHANNEL_BATTERY_STATE_OF_CHARGE, CHANNEL_FEED_IN_POWER, CHANNEL_TIMESTAMP,
+            CHANNEL_RAW_DATA, CHANNEL_INVERTER_OUTPUT_POWER_PHASE1, CHANNEL_INVERTER_OUTPUT_POWER_PHASE2,
+            CHANNEL_INVERTER_OUTPUT_POWER_PHASE3, CHANNEL_INVERTER_TOTAL_OUTPUT_POWER,
+            CHANNEL_INVERTER_OUTPUT_CURRENT_PHASE1, CHANNEL_INVERTER_OUTPUT_CURRENT_PHASE2,
+            CHANNEL_INVERTER_OUTPUT_CURRENT_PHASE3, CHANNEL_INVERTER_OUTPUT_VOLTAGE_PHASE1,
+            CHANNEL_INVERTER_OUTPUT_VOLTAGE_PHASE2, CHANNEL_INVERTER_OUTPUT_VOLTAGE_PHASE3,
+            CHANNEL_INVERTER_OUTPUT_FREQUENCY_PHASE1, CHANNEL_INVERTER_OUTPUT_FREQUENCY_PHASE2,
+            CHANNEL_INVERTER_OUTPUT_FREQUENCY_PHASE3, CHANNEL_POWER_USAGE, CHANNEL_TOTAL_ENERGY,
+            CHANNEL_TOTAL_BATTERY_CHARGE_ENERGY, CHANNEL_TOTAL_PV_ENERGY, CHANNEL_TOTAL_CONSUMPTION,
+            CHANNEL_TODAY_ENERGY, CHANNEL_TODAY_FEED_IN_ENERGY, CHANNEL_TODAY_CONSUMPTION,
+            CHANNEL_TODAY_BATTERY_CHARGE_ENERGY, CHANNEL_TODAY_BATTERY_DISCHARGE_ENERGY, CHANNEL_INVERTER_WORKMODE);
+
+    @Override
+    public LocalData getData(LocalConnectRawDataBean rawData) {
+        return new X3HybridG4Data(rawData);
+    }
+
+    @Override
+    public Set<String> getSupportedChannels() {
+        return X3_HYBRID_G4_SUPPORTED_CHANNELS;
+    }
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/inverters/X3MicOrProG2DataParser.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/inverters/X3MicOrProG2DataParser.java
new file mode 100644 (file)
index 0000000..f63c70c
--- /dev/null
@@ -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.model.local.inverters;
+
+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.LocalData;
+import org.openhab.binding.solax.internal.model.local.RawDataParser;
+import org.openhab.binding.solax.internal.model.local.X3MicOrProG2Data;
+
+/**
+ * The {@link X3MicOrProG2DataParser} is the implementation that parses raw data into a SinglePhaseInverterData for the
+ * X3 Mic / Pro G2 inverter.
+ *
+ * @author Henrik Tóth - Initial contribution
+ *         (based on X1/X3 G4 parser from Konstantin Polihronov)
+ */
+@NonNullByDefault
+public class X3MicOrProG2DataParser implements RawDataParser {
+
+    private static final Set<String> X3_MIC_OR_PRO_G2_SUPPORTED_CHANNELS = Set.of(
+            CHANNEL_INVERTER_OUTPUT_VOLTAGE_PHASE1, CHANNEL_INVERTER_OUTPUT_FREQUENCY_PHASE2,
+            CHANNEL_INVERTER_OUTPUT_VOLTAGE_PHASE3, CHANNEL_INVERTER_OUTPUT_CURRENT_PHASE1,
+            CHANNEL_INVERTER_OUTPUT_CURRENT_PHASE2, CHANNEL_INVERTER_OUTPUT_CURRENT_PHASE3,
+            CHANNEL_INVERTER_OUTPUT_POWER_PHASE1, CHANNEL_INVERTER_OUTPUT_POWER_PHASE2,
+            CHANNEL_INVERTER_OUTPUT_POWER_PHASE3, CHANNEL_INVERTER_PV1_VOLTAGE, CHANNEL_INVERTER_PV2_VOLTAGE,
+            CHANNEL_INVERTER_PV1_CURRENT, CHANNEL_INVERTER_PV2_CURRENT, CHANNEL_INVERTER_PV1_POWER,
+            CHANNEL_INVERTER_PV2_POWER, CHANNEL_INVERTER_OUTPUT_FREQUENCY_PHASE1,
+            CHANNEL_INVERTER_OUTPUT_VOLTAGE_PHASE2, CHANNEL_INVERTER_OUTPUT_FREQUENCY_PHASE3, CHANNEL_TOTAL_ENERGY,
+            CHANNEL_TODAY_ENERGY, CHANNEL_INVERTER_TOTAL_OUTPUT_POWER, CHANNEL_INVERTER_TEMPERATURE1,
+            CHANNEL_INVERTER_TEMPERATURE2, CHANNEL_INVERTER_WORKMODE, CHANNEL_RAW_DATA);
+
+    @Override
+    public LocalData getData(LocalConnectRawDataBean rawData) {
+        return new X3MicOrProG2Data(rawData);
+    }
+
+    @Override
+    public Set<String> getSupportedChannels() {
+        return X3_MIC_OR_PRO_G2_SUPPORTED_CHANNELS;
+    }
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/RawDataParser.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/RawDataParser.java
deleted file mode 100644 (file)
index d45cc08..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * 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 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;
-
-/**
- * The {@link RawDataParser} declares generic parser implementation that parses raw data to generic inverter data which
- * is common for all inverters.
- *
- * @author Konstantin Polihronov - Initial contribution
- */
-@NonNullByDefault
-public interface RawDataParser {
-
-    LocalInverterData getData(LocalConnectRawDataBean bean);
-
-    Set<String> getSupportedChannels();
-}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/X1BoostAirMiniDataParser.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/X1BoostAirMiniDataParser.java
deleted file mode 100644 (file)
index 4b680b6..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- * 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;
-    }
-}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/X1HybridG4DataParser.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/X1HybridG4DataParser.java
deleted file mode 100644 (file)
index 2804f1d..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/**
- * 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.X1HybridG4InverterData;
-
-/**
- * The {@link X1HybridG4DataParser} is the implementation that parses raw data into a LocalInverterData for the
- * X1 Hybrid G4 inverter.
- *
- * @author Konstantin Polihronov - Initial contribution
- */
-@NonNullByDefault
-public class X1HybridG4DataParser implements RawDataParser {
-
-    private static final Set<String> X1_HYBRID_G4_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_BATTERY_POWER, CHANNEL_BATTERY_VOLTAGE, CHANNEL_BATTERY_CURRENT,
-            CHANNEL_BATTERY_TEMPERATURE, CHANNEL_BATTERY_STATE_OF_CHARGE, CHANNEL_FEED_IN_POWER, CHANNEL_TIMESTAMP,
-            CHANNEL_RAW_DATA, CHANNEL_INVERTER_OUTPUT_POWER, CHANNEL_INVERTER_OUTPUT_CURRENT,
-            CHANNEL_INVERTER_OUTPUT_VOLTAGE, CHANNEL_INVERTER_OUTPUT_FREQUENCY, CHANNEL_INVERTER_WORKMODE);
-
-    @Override
-    public LocalInverterData getData(LocalConnectRawDataBean rawData) {
-        return new X1HybridG4InverterData(rawData);
-    }
-
-    @Override
-    public Set<String> getSupportedChannels() {
-        return X1_HYBRID_G4_SUPPORTED_CHANNELS;
-    }
-}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/X3HybridG4DataParser.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/X3HybridG4DataParser.java
deleted file mode 100644 (file)
index 3cc4f11..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- * 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.X3HybridG4InverterData;
-
-/**
- * The {@link X3HybridG4DataParser} is the implementation that parses raw data into a LocalInverterData for the
- * X3 Hybrid G4 inverter.
- *
- * @author Konstantin Polihronov - Initial contribution
- */
-@NonNullByDefault
-public class X3HybridG4DataParser implements RawDataParser {
-
-    private static final Set<String> X3_HYBRID_G4_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_BATTERY_POWER, CHANNEL_BATTERY_VOLTAGE, CHANNEL_BATTERY_CURRENT,
-            CHANNEL_BATTERY_TEMPERATURE, CHANNEL_BATTERY_STATE_OF_CHARGE, CHANNEL_FEED_IN_POWER, CHANNEL_TIMESTAMP,
-            CHANNEL_RAW_DATA, CHANNEL_INVERTER_OUTPUT_POWER_PHASE1, CHANNEL_INVERTER_OUTPUT_POWER_PHASE2,
-            CHANNEL_INVERTER_OUTPUT_POWER_PHASE3, CHANNEL_INVERTER_TOTAL_OUTPUT_POWER,
-            CHANNEL_INVERTER_OUTPUT_CURRENT_PHASE1, CHANNEL_INVERTER_OUTPUT_CURRENT_PHASE2,
-            CHANNEL_INVERTER_OUTPUT_CURRENT_PHASE3, CHANNEL_INVERTER_OUTPUT_VOLTAGE_PHASE1,
-            CHANNEL_INVERTER_OUTPUT_VOLTAGE_PHASE2, CHANNEL_INVERTER_OUTPUT_VOLTAGE_PHASE3,
-            CHANNEL_INVERTER_OUTPUT_FREQUENCY_PHASE1, CHANNEL_INVERTER_OUTPUT_FREQUENCY_PHASE2,
-            CHANNEL_INVERTER_OUTPUT_FREQUENCY_PHASE3, CHANNEL_POWER_USAGE, CHANNEL_TOTAL_ENERGY,
-            CHANNEL_TOTAL_BATTERY_CHARGE_ENERGY, CHANNEL_TOTAL_PV_ENERGY, CHANNEL_TOTAL_CONSUMPTION,
-            CHANNEL_TODAY_ENERGY, CHANNEL_TODAY_FEED_IN_ENERGY, CHANNEL_TODAY_CONSUMPTION,
-            CHANNEL_TODAY_BATTERY_CHARGE_ENERGY, CHANNEL_TODAY_BATTERY_DISCHARGE_ENERGY, CHANNEL_INVERTER_WORKMODE);
-
-    @Override
-    public LocalInverterData getData(LocalConnectRawDataBean rawData) {
-        return new X3HybridG4InverterData(rawData);
-    }
-
-    @Override
-    public Set<String> getSupportedChannels() {
-        return X3_HYBRID_G4_SUPPORTED_CHANNELS;
-    }
-}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/X3MicOrProG2DataParser.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/local/parsers/X3MicOrProG2DataParser.java
deleted file mode 100644 (file)
index 4dec856..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/**
- * 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.X3MicOrProG2InverterData;
-
-/**
- * The {@link X3MicOrProG2DataParser} is the implementation that parses raw data into a SinglePhaseInverterData for the
- * X3 Mic / Pro G2 inverter.
- *
- * @author Henrik Tóth - Initial contribution
- *         (based on X1/X3 G4 parser from Konstantin Polihronov)
- */
-@NonNullByDefault
-public class X3MicOrProG2DataParser implements RawDataParser {
-
-    private static final Set<String> X3_MIC_OR_PRO_G2_SUPPORTED_CHANNELS = Set.of(
-            CHANNEL_INVERTER_OUTPUT_VOLTAGE_PHASE1, CHANNEL_INVERTER_OUTPUT_FREQUENCY_PHASE2,
-            CHANNEL_INVERTER_OUTPUT_VOLTAGE_PHASE3, CHANNEL_INVERTER_OUTPUT_CURRENT_PHASE1,
-            CHANNEL_INVERTER_OUTPUT_CURRENT_PHASE2, CHANNEL_INVERTER_OUTPUT_CURRENT_PHASE3,
-            CHANNEL_INVERTER_OUTPUT_POWER_PHASE1, CHANNEL_INVERTER_OUTPUT_POWER_PHASE2,
-            CHANNEL_INVERTER_OUTPUT_POWER_PHASE3, CHANNEL_INVERTER_PV1_VOLTAGE, CHANNEL_INVERTER_PV2_VOLTAGE,
-            CHANNEL_INVERTER_PV1_CURRENT, CHANNEL_INVERTER_PV2_CURRENT, CHANNEL_INVERTER_PV1_POWER,
-            CHANNEL_INVERTER_PV2_POWER, CHANNEL_INVERTER_OUTPUT_FREQUENCY_PHASE1,
-            CHANNEL_INVERTER_OUTPUT_VOLTAGE_PHASE2, CHANNEL_INVERTER_OUTPUT_FREQUENCY_PHASE3, CHANNEL_TOTAL_ENERGY,
-            CHANNEL_TODAY_ENERGY, CHANNEL_INVERTER_TOTAL_OUTPUT_POWER, CHANNEL_INVERTER_TEMPERATURE1,
-            CHANNEL_INVERTER_TEMPERATURE2, CHANNEL_INVERTER_WORKMODE, CHANNEL_RAW_DATA);
-
-    @Override
-    public LocalInverterData getData(LocalConnectRawDataBean rawData) {
-        return new X3MicOrProG2InverterData(rawData);
-    }
-
-    @Override
-    public Set<String> getSupportedChannels() {
-        return X3_MIC_OR_PRO_G2_SUPPORTED_CHANNELS;
-    }
-}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/util/ByteUtil.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/util/ByteUtil.java
new file mode 100644 (file)
index 0000000..dc5b598
--- /dev/null
@@ -0,0 +1,31 @@
+/**
+ * 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.util;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link ByteUtil} Utility method for manipulating byte-level data
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public class ByteUtil {
+
+    public static final int MASK = 0xFFFF;
+    public static final int SHIFT_VALUE = 16;
+
+    public static int read32BitSigned(short low, short high) {
+        return (high << SHIFT_VALUE) | (low & MASK);
+    }
+}
index e5f3ee6b0cd245cb14dbeb588f1dd23c5ad99a39..bd1c9106503cdf19b2c8da00e640a162956c1045 100644 (file)
@@ -23,6 +23,8 @@ thing-type.solax.cloud-connect-inverter.channel.inverter-meter2-power.label = Me
 thing-type.solax.cloud-connect-inverter.channel.inverter-meter2-power.description = Inverter power on meter2.
 thing-type.solax.cloud-connect-inverter.channel.inverter-output-power.label = Inverter Input/Output Power
 thing-type.solax.cloud-connect-inverter.channel.inverter-output-power.description = Power to/from the inverter
+thing-type.solax.cloud-connect-inverter.channel.inverter-workmode.label = Inverter Workmode
+thing-type.solax.cloud-connect-inverter.channel.inverter-workmode.description = Inverter Workmode
 thing-type.solax.cloud-connect-inverter.channel.pv-total-power.label = PV Total Power
 thing-type.solax.cloud-connect-inverter.channel.pv-total-power.description = The sum of PV powers from all PV strings
 thing-type.solax.cloud-connect-inverter.channel.pv1-power.label = PV 1 Power
@@ -41,6 +43,54 @@ thing-type.solax.cloud-connect-inverter.channel.total-energy.label = Yield total
 thing-type.solax.cloud-connect-inverter.channel.total-energy.description = Total inverter output energy
 thing-type.solax.cloud-connect-inverter.channel.total-feed-in-energy.label = Total Feed-In Energy
 thing-type.solax.cloud-connect-inverter.channel.total-feed-in-energy.description = Total energy feed-in to the electricity network.
+thing-type.solax.local-connect-charger.label = Local Connect Charger
+thing-type.solax.local-connect-charger.description = The charger representation that supports local connections via HTTP
+thing-type.solax.local-connect-charger.channel.charger-current-phase1.label = Charger Output Current Phase 1
+thing-type.solax.local-connect-charger.channel.charger-current-phase1.description = Current from the charger phase 1
+thing-type.solax.local-connect-charger.channel.charger-current-phase2.label = Charger Output Current Phase 2
+thing-type.solax.local-connect-charger.channel.charger-current-phase2.description = Current from the charger phase 2
+thing-type.solax.local-connect-charger.channel.charger-current-phase3.label = Charger Output Current Phase 3
+thing-type.solax.local-connect-charger.channel.charger-current-phase3.description = Current from the charger phase 3
+thing-type.solax.local-connect-charger.channel.charger-external-current-phase1.label = Charger External Current Phase 1
+thing-type.solax.local-connect-charger.channel.charger-external-current-phase1.description = Current from the provider phase 1
+thing-type.solax.local-connect-charger.channel.charger-external-current-phase2.label = Charger External Current Phase 2
+thing-type.solax.local-connect-charger.channel.charger-external-current-phase2.description = Current from the provider phase 2
+thing-type.solax.local-connect-charger.channel.charger-external-current-phase3.label = Charger External Current Phase 3
+thing-type.solax.local-connect-charger.channel.charger-external-current-phase3.description = Current from the provider phase 3
+thing-type.solax.local-connect-charger.channel.charger-external-power-phase1.label = Charger External Power Phase 1
+thing-type.solax.local-connect-charger.channel.charger-external-power-phase1.description = Power from the provider phase 1
+thing-type.solax.local-connect-charger.channel.charger-external-power-phase2.label = Charger External Power Phase 2
+thing-type.solax.local-connect-charger.channel.charger-external-power-phase2.description = Power from the provider phase 2
+thing-type.solax.local-connect-charger.channel.charger-external-power-phase3.label = Charger External Power Phase 3
+thing-type.solax.local-connect-charger.channel.charger-external-power-phase3.description = Power from the provider phase 3
+thing-type.solax.local-connect-charger.channel.charger-external-total-power.label = Charger External Total Power
+thing-type.solax.local-connect-charger.channel.charger-external-total-power.description = Total power from the provider
+thing-type.solax.local-connect-charger.channel.charger-internal-temperature.label = Charger Internal Temperature
+thing-type.solax.local-connect-charger.channel.charger-internal-temperature.description = Internal temperature on the board of the charger
+thing-type.solax.local-connect-charger.channel.charger-mode.label = Charger Workmode
+thing-type.solax.local-connect-charger.channel.charger-mode.description = Charger Workmode
+thing-type.solax.local-connect-charger.channel.charger-output-power-phase1.label = Charger Output Power Phase 1
+thing-type.solax.local-connect-charger.channel.charger-output-power-phase1.description = Power to/from the charger phase 1
+thing-type.solax.local-connect-charger.channel.charger-output-power-phase2.label = Charger Output Power Phase 2
+thing-type.solax.local-connect-charger.channel.charger-output-power-phase2.description = Power to/from the charger phase 2
+thing-type.solax.local-connect-charger.channel.charger-output-power-phase3.label = Charger Output Power Phase 3
+thing-type.solax.local-connect-charger.channel.charger-output-power-phase3.description = Power to/from the charger phase 3
+thing-type.solax.local-connect-charger.channel.charger-plug-temperature.label = Charger Plug Temperature
+thing-type.solax.local-connect-charger.channel.charger-plug-temperature.description = Temperature of the charger's plug
+thing-type.solax.local-connect-charger.channel.charger-state.label = Charger State
+thing-type.solax.local-connect-charger.channel.charger-state.description = Charger State
+thing-type.solax.local-connect-charger.channel.charger-total-output-power.label = Charger Output Total Power
+thing-type.solax.local-connect-charger.channel.charger-total-output-power.description = Power from the charger on all phases
+thing-type.solax.local-connect-charger.channel.charger-voltage-phase1.label = Charger Voltage Phase 1
+thing-type.solax.local-connect-charger.channel.charger-voltage-phase1.description = Voltage of the charger's phase 1
+thing-type.solax.local-connect-charger.channel.charger-voltage-phase2.label = Charger Voltage Phase 2
+thing-type.solax.local-connect-charger.channel.charger-voltage-phase2.description = Voltage of the charger's phase 2
+thing-type.solax.local-connect-charger.channel.charger-voltage-phase3.label = Charger Voltage Phase 3
+thing-type.solax.local-connect-charger.channel.charger-voltage-phase3.description = Voltage of the charger's phase 3
+thing-type.solax.local-connect-charger.channel.eq-single-session.label = Energy charged this session
+thing-type.solax.local-connect-charger.channel.eq-single-session.description = The energy charged for the current session
+thing-type.solax.local-connect-charger.channel.eq-total.label = Total energy charged
+thing-type.solax.local-connect-charger.channel.eq-total.description = The total energy charged for all sessions
 thing-type.solax.local-connect-inverter.label = Local Connect Inverter
 thing-type.solax.local-connect-inverter.description = The inverter representation that supports local connections via HTTP
 thing-type.solax.local-connect-inverter.channel.battery-current.label = Battery Current
@@ -142,6 +192,12 @@ thing-type.config.solax.cloud-connect-inverter.refreshInterval.label = Refresh I
 thing-type.config.solax.cloud-connect-inverter.refreshInterval.description = Refresh interval in seconds. (Cloud API is limited to max 10 calls per minute and 10000 times per day)
 thing-type.config.solax.cloud-connect-inverter.token.label = Token
 thing-type.config.solax.cloud-connect-inverter.token.description = Token to access the Solax cloud API
+thing-type.config.solax.local-connect-charger.hostname.label = Network Address
+thing-type.config.solax.local-connect-charger.hostname.description = IP address or the host name of the Wi-Fi module
+thing-type.config.solax.local-connect-charger.password.label = Password
+thing-type.config.solax.local-connect-charger.password.description = Password for accessing the Wi-Fi module (the serial number of the Wi-Fi module)
+thing-type.config.solax.local-connect-charger.refreshInterval.label = Refresh Interval
+thing-type.config.solax.local-connect-charger.refreshInterval.description = Specifies the refresh interval in seconds.
 thing-type.config.solax.local-connect-inverter.hostname.label = Network Address
 thing-type.config.solax.local-connect-inverter.hostname.description = IP address or the host name of the Wi-Fi module
 thing-type.config.solax.local-connect-inverter.password.label = Password
@@ -153,12 +209,45 @@ thing-type.config.solax.local-connect-inverter.refreshInterval.description = Spe
 
 channel-type.solax.battery-temperature.label = Battery Temperature
 channel-type.solax.battery-temperature.description = Battery Temperature
+channel-type.solax.charger-mode.label = Charger Mode
+channel-type.solax.charger-mode.description = Charger Mode
+channel-type.solax.charger-mode.state.option.0 = Stop
+channel-type.solax.charger-mode.state.option.1 = Fast
+channel-type.solax.charger-mode.state.option.2 = Eco
+channel-type.solax.charger-mode.state.option.3 = Green
+channel-type.solax.charger-state.label = Charger State
+channel-type.solax.charger-state.description = Charger State
+channel-type.solax.charger-state.state.option.0 = Preparing
+channel-type.solax.charger-state.state.option.1 = Preparing
+channel-type.solax.charger-state.state.option.2 = Charging
+channel-type.solax.charger-state.state.option.3 = Finishing
+channel-type.solax.charger-state.state.option.4 = Fault
+channel-type.solax.charger-state.state.option.5 = Unavailable
+channel-type.solax.charger-state.state.option.6 = Reserved
+channel-type.solax.charger-state.state.option.7 = Suspended EV
+channel-type.solax.charger-state.state.option.8 = Suspended EV SE
+channel-type.solax.charger-temperature.label = Charger Temperature
+channel-type.solax.charger-temperature.description = Charger Temperature
 channel-type.solax.frequency.label = Electric Frequency
 channel-type.solax.frequency.description = Frequency of the electricity to/from the inverter
-channel-type.solax.inverter-status-type.label = Inverter Status
-channel-type.solax.inverter-status-type.description = The status of the inverter.
 channel-type.solax.inverter-temperature.label = Inverter Temperature
 channel-type.solax.inverter-temperature.description = Inverter Temperature
+channel-type.solax.inverter-workmode-cloud.label = Inverter Workmode
+channel-type.solax.inverter-workmode-cloud.description = Inverter Workmode
+channel-type.solax.inverter-workmode-cloud.state.option.100 = Waiting
+channel-type.solax.inverter-workmode-cloud.state.option.101 = Checking
+channel-type.solax.inverter-workmode-cloud.state.option.102 = Normal
+channel-type.solax.inverter-workmode-cloud.state.option.103 = Fault
+channel-type.solax.inverter-workmode-cloud.state.option.104 = Permanent Fault
+channel-type.solax.inverter-workmode-cloud.state.option.105 = Updating
+channel-type.solax.inverter-workmode-cloud.state.option.106 = EPS Check
+channel-type.solax.inverter-workmode-cloud.state.option.107 = EPS Normal
+channel-type.solax.inverter-workmode-cloud.state.option.108 = Self Test
+channel-type.solax.inverter-workmode-cloud.state.option.109 = Idle
+channel-type.solax.inverter-workmode-cloud.state.option.110 = Standby
+channel-type.solax.inverter-workmode-cloud.state.option.111 = PV Wake-up Battery
+channel-type.solax.inverter-workmode-cloud.state.option.112 = GEN Check
+channel-type.solax.inverter-workmode-cloud.state.option.113 = GEN Run
 channel-type.solax.inverter-workmode.label = Inverter Workmode
 channel-type.solax.inverter-workmode.description = Inverter Workmode
 channel-type.solax.inverter-workmode.state.option.0 = Waiting
@@ -180,6 +269,11 @@ channel-type.solax.last-retrieve-time-stamp.description = Last time with a succe
 channel-type.solax.raw-data-type.label = Raw Data
 channel-type.solax.raw-data-type.description = The raw JSON data retrieved from the inverter's Wi-Fi module.
 
+# channel types
+
+channel-type.solax.inverter-status-type.label = Inverter Status
+channel-type.solax.inverter-status-type.description = The status of the inverter.
+
 # thing status descriptions
 
 cloud.inverter.status.wait = Wait
@@ -198,3 +292,4 @@ cloud.inverter.status.gen-run = Gen Run
 cloud.inverter.status.unknown = Unknown
 offline.communication-error.json-cannot-be-retrieved = JSON data could not be retrieved.
 offline.configuration-error.parser-not-implemented = Parser for inverter of type {0} is not implemented.
+offline.configuration-error.json-cannot-be-parsed = Json cannot be parsed. Check your connection configuration for a wrong password.
index 2267461b2e7acbc445612d4b2d0711c8fa5218cb..20af98eb435ea5c7b9f180957cddd5ee88d55860 100644 (file)
                <description>The raw JSON data retrieved from the inverter's Wi-Fi module.</description>
                <state readOnly="true"/>
        </channel-type>
+       <channel-type id="charger-state">
+               <item-type>String</item-type>
+               <label>Charger State</label>
+               <description>Charger State</description>
+               <state pattern="%s" readOnly="true">
+                       <options>
+                               <option value="0">Preparing</option>
+                               <option value="1">Preparing</option>
+                               <option value="2">Charging</option>
+                               <option value="3">Finishing</option>
+                               <option value="4">Fault</option>
+                               <option value="5">Unavailable</option>
+                               <option value="6">Reserved</option>
+                               <option value="7">Suspended EV</option>
+                               <option value="8">Suspended EV SE</option>
+                       </options>
+               </state>
+       </channel-type>
+       <channel-type id="charger-mode">
+               <item-type>String</item-type>
+               <label>Charger Mode</label>
+               <description>Charger Mode</description>
+               <state pattern="%s" readOnly="true">
+                       <options>
+                               <option value="0">Stop</option>
+                               <option value="1">Fast</option>
+                               <option value="2">Eco</option>
+                               <option value="3">Green</option>
+                       </options>
+               </state>
+       </channel-type>
+       <channel-type id="charger-temperature">
+               <item-type>Number:Temperature</item-type>
+               <label>Charger Temperature</label>
+               <description>Charger Temperature</description>
+               <tags>
+                       <tag>Measurement</tag>
+                       <tag>Temperature</tag>
+               </tags>
+               <state pattern="%d %unit%" readOnly="true"/>
+       </channel-type>
 
 </thing:thing-descriptions>
diff --git a/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/thing/localConnectEVCharger.xml b/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/thing/localConnectEVCharger.xml
new file mode 100644 (file)
index 0000000..51325c9
--- /dev/null
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="solax"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
+       xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
+
+       <thing-type id="local-connect-charger">
+
+               <label>Local Connect Charger</label>
+               <description>The charger representation that supports local connections via HTTP</description>
+
+               <channels>
+                       <channel id="eq-single-session" typeId="system.electric-energy">
+                               <label>Energy This Session</label>
+                               <description>The energy charged for the current session</description>
+                       </channel>
+                       <channel id="eq-total" typeId="system.electric-energy">
+                               <label>Total Energy Charged</label>
+                               <description>The total energy charged for all sessions</description>
+                       </channel>
+                       <channel id="charger-output-power-phase1" typeId="system.electric-power">
+                               <label>Output Power Phase1</label>
+                               <description>Power to/from the charger phase 1</description>
+                       </channel>
+                       <channel id="charger-output-power-phase2" typeId="system.electric-power">
+                               <label>Output Power Phase2</label>
+                               <description>Power to/from the charger phase 2</description>
+                       </channel>
+                       <channel id="charger-output-power-phase3" typeId="system.electric-power">
+                               <label>Output Power Phase3</label>
+                               <description>Power to/from the charger phase 3</description>
+                       </channel>
+                       <channel id="charger-total-output-power" typeId="system.electric-power">
+                               <label>Output Total Power</label>
+                               <description>Power from the charger on all phases</description>
+                       </channel>
+                       <channel id="charger-current-phase1" typeId="system.electric-current">
+                               <label>Output Current Phase1</label>
+                               <description>Current from the charger phase 1</description>
+                       </channel>
+                       <channel id="charger-current-phase2" typeId="system.electric-current">
+                               <label>Output Current Phase2</label>
+                               <description>Current from the charger phase 2</description>
+                       </channel>
+                       <channel id="charger-current-phase3" typeId="system.electric-current">
+                               <label>Output Current Phase3</label>
+                               <description>Current from the charger phase 3</description>
+                       </channel>
+                       <channel id="charger-voltage-phase1" typeId="system.electric-voltage">
+                               <label>Voltage Phase1</label>
+                               <description>Voltage of the charger's phase 1</description>
+                       </channel>
+                       <channel id="charger-voltage-phase2" typeId="system.electric-voltage">
+                               <label>Voltage Phase2</label>
+                               <description>Voltage of the charger's phase 2</description>
+                       </channel>
+                       <channel id="charger-voltage-phase3" typeId="system.electric-voltage">
+                               <label>Voltage Phase3</label>
+                               <description>Voltage of the charger's phase 3</description>
+                       </channel>
+
+                       <channel id="charger-external-current-phase1" typeId="system.electric-current">
+                               <label>External Current Phase1</label>
+                               <description>Current from the provider phase 1</description>
+                       </channel>
+                       <channel id="charger-external-current-phase2" typeId="system.electric-current">
+                               <label>External Current Phase2</label>
+                               <description>Current from the provider phase 2</description>
+                       </channel>
+                       <channel id="charger-external-current-phase3" typeId="system.electric-current">
+                               <label>External Current Phase3</label>
+                               <description>Current from the provider phase 3</description>
+                       </channel>
+                       <channel id="charger-external-power-phase1" typeId="system.electric-power">
+                               <label>External Power Phase1</label>
+                               <description>Power from the provider phase 1</description>
+                       </channel>
+                       <channel id="charger-external-power-phase2" typeId="system.electric-power">
+                               <label>External Power Phase2</label>
+                               <description>Power from the provider phase 2</description>
+                       </channel>
+                       <channel id="charger-external-power-phase3" typeId="system.electric-power">
+                               <label>External Power Phase3</label>
+                               <description>Power from the provider phase 3</description>
+                       </channel>
+                       <channel id="charger-external-total-power" typeId="system.electric-power">
+                               <label>External Total Power</label>
+                               <description>Total power from the provider</description>
+                       </channel>
+
+                       <channel id="charger-plug-temperature" typeId="charger-temperature">
+                               <label>Plug Temperature</label>
+                               <description>Temperature of the charger's plug</description>
+                       </channel>
+                       <channel id="charger-internal-temperature" typeId="charger-temperature">
+                               <label>Internal Temperature</label>
+                               <description>Internal temperature on the board of the charger</description>
+                       </channel>
+                       <channel id="charger-mode" typeId="charger-mode">
+                               <label>Workmode</label>
+                               <description>Charger Workmode</description>
+                       </channel>
+                       <channel id="charger-state" typeId="charger-state">
+                               <label>State</label>
+                               <description>Charger State</description>
+                       </channel>
+
+                       <channel id="last-update-time" typeId="last-retrieve-time-stamp"/>
+               </channels>
+
+               <config-description>
+                       <parameter name="refreshInterval" type="integer" min="1" max="600">
+                               <label>Refresh Interval</label>
+                               <description>Specifies the refresh interval in seconds.</description>
+                               <default>10</default>
+                       </parameter>
+                       <parameter name="password" type="text" required="true">
+                               <label>Password</label>
+                               <description>Password for accessing the Wi-Fi module (the serial number of the Wi-Fi module)</description>
+                               <context>password</context>
+                       </parameter>
+                       <parameter name="hostname" type="text" required="true">
+                               <label>Network Address</label>
+                               <description>IP address or the host name of the Wi-Fi module</description>
+                               <context>network-address</context>
+                       </parameter>
+               </config-description>
+       </thing-type>
+</thing:thing-descriptions>
index 791e304ba422ee36b0231b44f78aea243caece8e..3b840a8972f7e98bc1264a8cbb654d779c6921c6 100644 (file)
@@ -20,8 +20,8 @@ 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;
+import org.openhab.binding.solax.internal.model.local.LocalData;
+import org.openhab.binding.solax.internal.model.local.RawDataParser;
 
 /**
  * The {@link AbstractParserTest} Abstract class defining the common logic for testing local connections to the various
@@ -44,7 +44,7 @@ public abstract class AbstractParserTest {
         Set<String> supportedChannels = parser.getSupportedChannels();
         assertFalse(supportedChannels.isEmpty());
 
-        LocalInverterData data = parser.getData(bean);
+        LocalData data = parser.getData(bean);
         assertParserSpecific(data);
     }
 
@@ -52,5 +52,5 @@ public abstract class AbstractParserTest {
 
     protected abstract String getRawData();
 
-    protected abstract void assertParserSpecific(LocalInverterData data);
+    protected abstract void assertParserSpecific(LocalData data);
 }
diff --git a/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/TestX1BoostAirMiniDataParser.java b/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/TestX1BoostAirMiniDataParser.java
deleted file mode 100644 (file)
index 66f6f4b..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-/**
- * 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;
-    }
-}
diff --git a/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/TestX1HybridG4Parser.java b/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/TestX1HybridG4Parser.java
deleted file mode 100644 (file)
index f2d2032..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/**
- * 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 TestX1HybridG4Parser} Simple test that tests for proper parsing against a real data from the inverter
- *
- * @author Konstantin Polihronov - Initial contribution
- */
-@NonNullByDefault
-public class TestX1HybridG4Parser extends AbstractParserTest {
-
-    private static final String RAW_DATA = """
-            {
-                sn:SOME_SERIAL_NUMBER,
-                ver:3.008.10,
-                type:15,
-                Data:[
-                    2388,21,460,4998,4483,4483,10,1,487,65,
-                    2,59781,0,70,12180,500,605,33,99,12000,
-                    0,23159,0,57,100,0,39,4501,0,0,
-                    0,0,12,0,13240,0,63348,2,448,43,
-                    256,1314,900,0,350,311,279,33,33,279,1,1,652,0,708,1,65077,65535,65386,65535,0,0,0,0,0,0,0,0,0,0,0,0,65068,65535,4500,0,61036,65535,10,0,90,0,0,12,0,116,7,57,0,0,2320,0,110,0,0,0,0,0,0,12544,7440,5896,594,521,9252,0,0,0,0,0,1,1201,0,0,3342,3336,7296,54,21302,14389,18753,12852,16692,12355,13618,21302,14389,18753,12852,16692,12355,13618,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1025,4609,1026,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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:[7.500,15,H4752TI1063020,8,1.24,0.00,1.21,1.03,0.00,1]}
-            """;
-
-    @Override
-    protected InverterType getInverterType() {
-        return InverterType.X1_HYBRID_G4;
-    }
-
-    @Override
-    protected void assertParserSpecific(LocalInverterData data) {
-        assertEquals("SOME_SERIAL_NUMBER", data.getWifiSerial());
-        assertEquals("3.008.10", data.getWifiVersion());
-
-        assertEquals(238.8, data.getInverterVoltage()); // [0]
-        assertEquals(2.1, data.getInverterCurrent()); // [1]
-        assertEquals(460, data.getInverterOutputPower()); // [2]
-        assertEquals(49.98, data.getInverterFrequency()); // [3]
-
-        assertEquals(448.3, data.getPV1Voltage()); // [4]
-        assertEquals(448.3, data.getPV2Voltage()); // [5]
-        assertEquals(1, data.getPV1Current()); // [6]
-        assertEquals(0.1, data.getPV2Current()); // [7]
-        assertEquals(487, data.getPV1Power()); // [8]
-        assertEquals(65, data.getPV2Power()); // [9]
-
-        assertEquals(2, data.getInverterWorkModeCode()); // [10]
-        assertEquals("2", data.getInverterWorkMode()); // [10]
-
-        assertEquals(121.8, data.getBatteryVoltage()); // [14]
-        assertEquals(5, data.getBatteryCurrent()); // [15]
-        assertEquals(605, data.getBatteryPower()); // [16]
-        assertEquals(33, data.getBatteryTemperature()); // [17]
-        assertEquals(99, data.getBatteryLevel()); // [18]
-
-        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],
-    // PowerDc2: Data[9],
-    // BAT_Power: read16BitSigned(Data[16]),
-    // feedInPower: read32BitSigned(Data[32], Data[33]),
-    // GridAPower: read16BitSigned(Data[2]),
-    // FeedInEnergy: read32BitUnsigned(Data[34], Data[35]) / 100,
-    // ConsumeEnergy: read32BitUnsigned(Data[36], Data[37]) / 100,
-    // RunMode: Data[10],
-    // EPSAPower: read16BitSigned(Data[28]),
-    // Vdc1: Data[4] / 10,
-    // Vdc2: Data[5] / 10,
-    // Idc1: Data[6] / 10,
-    // Idc2: Data[7] / 10,
-    // EPSAVoltage: Data[29] / 10,
-    // EPSACurrent: read16BitSigned(Data[30]) / 10,
-    // BatteryCapacity: Data[18],
-    // BatteryVoltage: Data[14] / 100,
-    // BatteryTemperature: read16BitSigned(Data[17]),
-    // GridAVoltage: Data[0] / 10,
-    // GridACurrent: read16BitSigned(Data[1]) / 10,
-    // FreqacA: Data[3] / 100,
-}
diff --git a/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/TestX3HybridG4Parser.java b/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/TestX3HybridG4Parser.java
deleted file mode 100644 (file)
index a30172c..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/**
- * 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 TestX3HybridG4Parser} simple test that tests for proper parsing against a real data from the inverter
- *
- * @author Konstantin Polihronov - Initial contribution
- */
-@NonNullByDefault
-public class TestX3HybridG4Parser extends AbstractParserTest {
-
-    private static final String RAW_DATA = """
-            {
-                sn:XYZ,
-                ver:3.005.01,
-                type:14,Data:[
-                    2316,2329,2315,18,18,18,372,363,365,1100,
-                    12,23,34,45,56,67,4996,4996,4996,2,
-                    0,0,0,0,0,0,0,0,0,0,
-                    0,0,0,1,65494,65535,0,0,0,31330,
-                    320,1034,3078,1,44,1100,256,1294,0,0,
-                    7445,5895,100,0,38,0,0,0,0,0,
-                    0,0,0,0,0,0,0,0,505,0,
-                    396,0,0,0,102,0,142,0,62,110,
-                    570,0,463,0,0,0,1925,0,369,0,
-                    506,1925,304,309,0,0,0,0,0,0,
-                    0,0,0,45,1,59,1,34,54,256,
-                    3504,2400,300,300,295,276,33,33,2,1620,779,15163,15163,14906,0,0,0,3270,3264,45581,0,20564,12339,18753,12353,18742,12356,13625,20564,12339,18754,12866,18743,14151,13104,20564,12339,18754,12866,18743,14151,12592,20564,12339,18754,12865,18738,12871,13620,0,0,0,0,0,0,0,1025,8195,769,259,0,31460,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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:[12.000,14,XY,8,1.23,0.00,1.24,1.09,0.00,1]
-             }
-            """;
-
-    @Override
-    protected InverterType getInverterType() {
-        return InverterType.X3_HYBRID_G4;
-    }
-
-    @Override
-    protected void assertParserSpecific(LocalInverterData data) {
-        assertEquals("XYZ", data.getWifiSerial());
-        assertEquals("3.005.01", data.getWifiVersion());
-
-        assertEquals(231.6, data.getVoltagePhase1()); // [0]
-        assertEquals(232.9, data.getVoltagePhase2()); // [1]
-        assertEquals(231.5, data.getVoltagePhase3()); // [2]
-
-        assertEquals(1.8, data.getCurrentPhase1()); // [3]
-        assertEquals(1.8, data.getCurrentPhase2()); // [4]
-        assertEquals(1.8, data.getCurrentPhase3()); // [5]
-
-        assertEquals(372, data.getOutputPowerPhase1()); // [6]
-        assertEquals(363, data.getOutputPowerPhase2()); // [7]
-        assertEquals(365, data.getOutputPowerPhase3()); // [8]
-
-        assertEquals(1100, data.getTotalOutputPower()); // [9]
-
-        assertEquals(1.2, data.getPV1Voltage()); // [10]
-        assertEquals(2.3, data.getPV2Voltage()); // [11]
-        assertEquals(3.4, data.getPV1Current()); // [12]
-        assertEquals(4.5, data.getPV2Current()); // [13]
-        assertEquals(56, data.getPV1Power()); // [14]
-        assertEquals(67, data.getPV2Power()); // [15]
-
-        assertEquals(49.96, data.getFrequencyPhase1()); // [16]
-        assertEquals(49.96, data.getFrequencyPhase2()); // [17]
-        assertEquals(49.96, data.getFrequencyPhase3()); // [18]
-
-        assertEquals(2, data.getInverterWorkModeCode()); // [19]
-        assertEquals("2", data.getInverterWorkMode()); // [19]
-
-        assertEquals(-41, data.getFeedInPower()); // [34] - [35]
-
-        assertEquals(313.3, data.getBatteryVoltage()); // [39]
-        assertEquals(3.2, data.getBatteryCurrent()); // [40]
-        assertEquals(1034, data.getBatteryPower()); // [41]
-        assertEquals(45, data.getBatteryLevel()); // [103]
-        assertEquals(59, data.getBatteryTemperature()); // [105]
-
-        // Totals
-        assertEquals(1294, data.getPowerUsage()); // [47]
-        assertEquals(50.5, data.getTotalEnergy()); // [68]
-        assertEquals(102, data.getTotalBatteryDischargeEnergy()); // [74]
-        assertEquals(142, data.getTotalBatteryChargeEnergy()); // [76]
-        assertEquals(57, data.getTotalPVEnergy()); // [80]
-        assertEquals(1925, data.getTotalFeedInEnergy()); // [86]
-        assertEquals(36.9, data.getTotalConsumption()); // [88]
-        assertEquals(46.3, data.getTodayEnergy()); // [82] / 10
-        assertEquals(5.06, data.getTodayFeedInEnergy()); // [90] / 100
-        assertEquals(3.04, data.getTodayConsumption()); // [92] / 100
-        assertEquals(6.2, data.getTodayBatteryDischargeEnergy()); // [78] / 100
-        assertEquals(11, data.getTodayBatteryChargeEnergy()); // [79] / 100
-    }
-
-    @Override
-    protected String getRawData() {
-        return RAW_DATA;
-    }
-}
diff --git a/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/TestX3MicOrProG2Parser.java b/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/TestX3MicOrProG2Parser.java
deleted file mode 100644 (file)
index 29846be..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/**
- * 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 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
- *
- * @author Konstantin Polihronov - Initial contribution
- */
-@NonNullByDefault
-public class TestX3MicOrProG2Parser {
-
-    String rawData = """
-            {
-                sn:XYZ,
-                ver:3.003.02,
-                type:16,Data:[
-                    2515,2449,2484,5,5,9,54,44,20,4080,4340,
-                    0,1,2,0,67,102,0,4999,4999,4999,2,19035,
-                    0,50,8000,5,9,
-                    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-                    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,
-                    0,0,0,0,0,0,0,0,120,40,1,6,5,0,6772,0,0,0,
-                    0,0,0,0,0,0,0,0,0,0,0,0
-                    ],
-                Information:[8.000,16,XY,8,1.15,0.00,1.11,1.01,0.00,1]
-             }
-            """;
-
-    @Test
-    public void testParser() {
-        LocalConnectRawDataBean bean = LocalConnectRawDataBean.fromJson(rawData);
-        int type = bean.getType();
-        InverterType inverterType = InverterType.fromIndex(type);
-        assertEquals(InverterType.X3_MIC_OR_PRO_G2, inverterType, "Inverter type not recognized properly");
-
-        RawDataParser parser = inverterType.getParser();
-        assertNotNull(parser);
-
-        LocalInverterData data = parser.getData(bean);
-        assertEquals("XYZ", data.getWifiSerial());
-        assertEquals("3.003.02", data.getWifiVersion());
-
-        assertEquals(251.5, data.getVoltagePhase1()); // [0]
-        assertEquals(244.9, data.getVoltagePhase2()); // [1]
-        assertEquals(248.4, data.getVoltagePhase3()); // [2]
-
-        assertEquals(0.5, data.getCurrentPhase1()); // [3]
-        assertEquals(0.5, data.getCurrentPhase2()); // [4]
-        assertEquals(0.9, data.getCurrentPhase3()); // [5]
-
-        assertEquals(54, data.getOutputPowerPhase1()); // [6]
-        assertEquals(44, data.getOutputPowerPhase2()); // [7]
-        assertEquals(20, data.getOutputPowerPhase3()); // [8]
-
-        assertEquals(408, data.getPV1Voltage()); // [9]
-        assertEquals(434, data.getPV2Voltage()); // [10]
-        assertEquals(0.1, data.getPV1Current()); // [12]
-        assertEquals(0.2, data.getPV2Current()); // [13]
-        assertEquals(67, data.getPV1Power()); // [15]
-        assertEquals(102, data.getPV2Power()); // [16]
-
-        assertEquals(49.99, data.getFrequencyPhase1()); // [18]
-        assertEquals(49.99, data.getFrequencyPhase2()); // [19]
-        assertEquals(49.99, data.getFrequencyPhase3()); // [20]
-
-        assertEquals(2, data.getInverterWorkModeCode()); // [21]
-        assertEquals("2", data.getInverterWorkMode()); // [21]
-
-        assertEquals(5, data.getInverterTemperature1()); // [26]
-        assertEquals(9, data.getInverterTemperature2()); // [27]
-
-        assertEquals(120, data.getTotalOutputPower()); // [78]
-
-        assertEquals(1903.5, data.getTotalEnergy()); // [22]
-        assertEquals(5.0, data.getTodayEnergy()); // [24]
-    }
-}
diff --git a/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestEVChargerParser.java b/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestEVChargerParser.java
new file mode 100644 (file)
index 0000000..6bbce4e
--- /dev/null
@@ -0,0 +1,101 @@
+/**
+ * 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.parsers;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+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.local.EvChargerData;
+
+/**
+ * The {@link TestEVChargerParser} Simple test that tests for proper parsing against a real data from the inverter
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public class TestEVChargerParser {
+
+    private static final String RAW_DATA = """
+            {
+                "SN":"SQBLABLA",
+                "ver":"3.004.11",
+                "type":1,
+                "Data":[
+                    2,2,23914,23991,23895,1517,1513,1519,3654,3657,
+                    3656,10968,44,0,346,0,65434,35463,65459,65508,
+                    65513,27,402,0,43,0,2,15,0,0,
+                    0,0,0,5004,5000,4996,10518,1547,6150,4,
+                    0,0,0,0,0,0,0,0,1,100,
+                    0,0,0,0,0,0,0,0,0,0,
+                    0,0,0,0,0,0,0,0,0,0,
+                    0,0,0,0,0,0,0,0,0,0,
+                    1717,0,3114,1547,6150,0,1,1,1,0,
+                    0,121,584,266,0,50,0,0,1,1,0],
+                "Information":[11.000,1,"CXXXXXXXXXX",1,1.13,1.01,0.00,0.00,0.00,1],
+                "OCPPServer":"",
+                "OCPPChargerId":""
+            }
+            """;
+
+    @Test
+    public void testParser() {
+        LocalConnectRawDataBean bean = LocalConnectRawDataBean.fromJson(RAW_DATA);
+        assertNotNull(bean);
+
+        EvChargerData data = new EvChargerData(bean);
+        assertEquals("SQBLABLA", data.getWifiSerial()); // 0
+        assertEquals("3.004.11", data.getWifiVersion()); // 1
+
+        assertEquals(239.14, data.getVoltagePhase1()); // 2
+        assertEquals(239.91, data.getVoltagePhase2()); // 3
+        assertEquals(238.95, data.getVoltagePhase3()); // 4
+
+        assertEquals(15.17, data.getCurrentPhase1()); // 5
+        assertEquals(15.13, data.getCurrentPhase2()); // 6
+        assertEquals(15.19, data.getCurrentPhase3()); // 7
+
+        assertEquals(3654, data.getOutputPowerPhase1()); // 8
+        assertEquals(3657, data.getOutputPowerPhase2()); // 9
+        assertEquals(3656, data.getOutputPowerPhase3()); // 10
+
+        assertEquals(10968, data.getTotalChargePower()); // 11
+
+        assertEquals(4.4, data.getEqSingle()); // 12
+        assertEquals(34.6, data.getEqTotal()); // 14 and 15
+
+        assertEquals(-1.02, data.getExternalCurrentPhase1()); // 16
+        assertEquals(-300.73, data.getExternalCurrentPhase2()); // 17
+        assertEquals(-0.77, data.getExternalCurrentPhase3()); // 18
+
+        assertEquals(-28, data.getExternalPowerPhase1()); // 19
+        assertEquals(-23, data.getExternalPowerPhase2()); // 20
+        assertEquals(27, data.getExternalPowerPhase3()); // 21
+        assertEquals(402, data.getExternalTotalPower()); // 22
+
+        assertEquals(0, data.getPlugTemperature()); // 23
+        assertEquals(43, data.getInternalTemperature()); // 24
+
+        assertEquals(2, data.getCPState()); // 26
+
+        assertEquals(1717, data.getChargingDuration()); // 80 and 81
+
+        assertEquals(0, data.getOccpOfflineMode()); // 85
+
+        assertEquals(1, data.getTypePower()); // 87
+        assertEquals(1, data.getTypePhase()); // 88
+        assertEquals(0, data.getTypeCharger()); // 89
+    }
+}
diff --git a/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestX1BoostAirMiniDataParser.java b/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestX1BoostAirMiniDataParser.java
new file mode 100644 (file)
index 0000000..395085d
--- /dev/null
@@ -0,0 +1,80 @@
+/**
+ * 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.parsers;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.solax.internal.local.AbstractParserTest;
+import org.openhab.binding.solax.internal.model.InverterType;
+import org.openhab.binding.solax.internal.model.local.LocalData;
+
+/**
+ * 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(LocalData 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;
+    }
+}
diff --git a/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestX1HybridG4Parser.java b/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestX1HybridG4Parser.java
new file mode 100644 (file)
index 0000000..90ea895
--- /dev/null
@@ -0,0 +1,106 @@
+/**
+ * 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.parsers;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.solax.internal.local.AbstractParserTest;
+import org.openhab.binding.solax.internal.model.InverterType;
+import org.openhab.binding.solax.internal.model.local.LocalData;
+
+/**
+ * The {@link TestX1HybridG4Parser} Simple test that tests for proper parsing against a real data from the inverter
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public class TestX1HybridG4Parser extends AbstractParserTest {
+
+    private static final String RAW_DATA = """
+            {
+                sn:SOME_SERIAL_NUMBER,
+                ver:3.008.10,
+                type:15,
+                Data:[
+                    2388,21,460,4998,4483,4483,10,1,487,65,
+                    2,59781,0,70,12180,500,605,33,99,12000,
+                    0,23159,0,57,100,0,39,4501,0,0,
+                    0,0,12,0,13240,0,63348,2,448,43,
+                    256,1314,900,0,350,311,279,33,33,279,1,1,652,0,708,1,65077,65535,65386,65535,0,0,0,0,0,0,0,0,0,0,0,0,65068,65535,4500,0,61036,65535,10,0,90,0,0,12,0,116,7,57,0,0,2320,0,110,0,0,0,0,0,0,12544,7440,5896,594,521,9252,0,0,0,0,0,1,1201,0,0,3342,3336,7296,54,21302,14389,18753,12852,16692,12355,13618,21302,14389,18753,12852,16692,12355,13618,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1025,4609,1026,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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:[7.500,15,H4752TI1063020,8,1.24,0.00,1.21,1.03,0.00,1]}
+            """;
+
+    @Override
+    protected InverterType getInverterType() {
+        return InverterType.X1_HYBRID_G4;
+    }
+
+    @Override
+    protected void assertParserSpecific(LocalData data) {
+        assertEquals("SOME_SERIAL_NUMBER", data.getWifiSerial());
+        assertEquals("3.008.10", data.getWifiVersion());
+
+        assertEquals(238.8, data.getInverterVoltage()); // [0]
+        assertEquals(2.1, data.getInverterCurrent()); // [1]
+        assertEquals(460, data.getInverterOutputPower()); // [2]
+        assertEquals(49.98, data.getInverterFrequency()); // [3]
+
+        assertEquals(448.3, data.getPV1Voltage()); // [4]
+        assertEquals(448.3, data.getPV2Voltage()); // [5]
+        assertEquals(1, data.getPV1Current()); // [6]
+        assertEquals(0.1, data.getPV2Current()); // [7]
+        assertEquals(487, data.getPV1Power()); // [8]
+        assertEquals(65, data.getPV2Power()); // [9]
+
+        assertEquals(2, data.getInverterWorkModeCode()); // [10]
+        assertEquals("2", data.getInverterWorkMode()); // [10]
+
+        assertEquals(121.8, data.getBatteryVoltage()); // [14]
+        assertEquals(5, data.getBatteryCurrent()); // [15]
+        assertEquals(605, data.getBatteryPower()); // [16]
+        assertEquals(33, data.getBatteryTemperature()); // [17]
+        assertEquals(99, data.getBatteryLevel()); // [18]
+
+        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],
+    // PowerDc2: Data[9],
+    // BAT_Power: read16BitSigned(Data[16]),
+    // feedInPower: read32BitSigned(Data[32], Data[33]),
+    // GridAPower: read16BitSigned(Data[2]),
+    // FeedInEnergy: read32BitUnsigned(Data[34], Data[35]) / 100,
+    // ConsumeEnergy: read32BitUnsigned(Data[36], Data[37]) / 100,
+    // RunMode: Data[10],
+    // EPSAPower: read16BitSigned(Data[28]),
+    // Vdc1: Data[4] / 10,
+    // Vdc2: Data[5] / 10,
+    // Idc1: Data[6] / 10,
+    // Idc2: Data[7] / 10,
+    // EPSAVoltage: Data[29] / 10,
+    // EPSACurrent: read16BitSigned(Data[30]) / 10,
+    // BatteryCapacity: Data[18],
+    // BatteryVoltage: Data[14] / 100,
+    // BatteryTemperature: read16BitSigned(Data[17]),
+    // GridAVoltage: Data[0] / 10,
+    // GridACurrent: read16BitSigned(Data[1]) / 10,
+    // FreqacA: Data[3] / 100,
+}
diff --git a/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestX3HybridG4Parser.java b/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestX3HybridG4Parser.java
new file mode 100644 (file)
index 0000000..056d2bc
--- /dev/null
@@ -0,0 +1,116 @@
+/**
+ * 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.parsers;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.solax.internal.local.AbstractParserTest;
+import org.openhab.binding.solax.internal.model.InverterType;
+import org.openhab.binding.solax.internal.model.local.LocalData;
+
+/**
+ * The {@link TestX3HybridG4Parser} simple test that tests for proper parsing against a real data from the inverter
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public class TestX3HybridG4Parser extends AbstractParserTest {
+
+    private static final String RAW_DATA = """
+            {
+                sn:XYZ,
+                ver:3.005.01,
+                type:14,Data:[
+                    2316,2329,2315,18,18,18,372,363,365,1100,
+                    12,23,34,45,56,67,4996,4996,4996,2,
+                    0,0,0,0,0,0,0,0,0,0,
+                    0,0,0,1,65494,65535,0,0,0,31330,
+                    320,1034,3078,1,44,1100,256,1294,0,0,
+                    7445,5895,100,0,38,0,0,0,0,0,
+                    0,0,0,0,0,0,0,0,505,0,
+                    396,0,0,0,102,0,142,0,62,110,
+                    570,0,463,0,0,0,1925,0,369,0,
+                    506,1925,304,309,0,0,0,0,0,0,
+                    0,0,0,45,1,59,1,34,54,256,
+                    3504,2400,300,300,295,276,33,33,2,1620,779,15163,15163,14906,0,0,0,3270,3264,45581,0,20564,12339,18753,12353,18742,12356,13625,20564,12339,18754,12866,18743,14151,13104,20564,12339,18754,12866,18743,14151,12592,20564,12339,18754,12865,18738,12871,13620,0,0,0,0,0,0,0,1025,8195,769,259,0,31460,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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:[12.000,14,XY,8,1.23,0.00,1.24,1.09,0.00,1]
+             }
+            """;
+
+    @Override
+    protected InverterType getInverterType() {
+        return InverterType.X3_HYBRID_G4;
+    }
+
+    @Override
+    protected void assertParserSpecific(LocalData data) {
+        assertEquals("XYZ", data.getWifiSerial());
+        assertEquals("3.005.01", data.getWifiVersion());
+
+        assertEquals(231.6, data.getVoltagePhase1()); // [0]
+        assertEquals(232.9, data.getVoltagePhase2()); // [1]
+        assertEquals(231.5, data.getVoltagePhase3()); // [2]
+
+        assertEquals(1.8, data.getCurrentPhase1()); // [3]
+        assertEquals(1.8, data.getCurrentPhase2()); // [4]
+        assertEquals(1.8, data.getCurrentPhase3()); // [5]
+
+        assertEquals(372, data.getOutputPowerPhase1()); // [6]
+        assertEquals(363, data.getOutputPowerPhase2()); // [7]
+        assertEquals(365, data.getOutputPowerPhase3()); // [8]
+
+        assertEquals(1100, data.getTotalOutputPower()); // [9]
+
+        assertEquals(1.2, data.getPV1Voltage()); // [10]
+        assertEquals(2.3, data.getPV2Voltage()); // [11]
+        assertEquals(3.4, data.getPV1Current()); // [12]
+        assertEquals(4.5, data.getPV2Current()); // [13]
+        assertEquals(56, data.getPV1Power()); // [14]
+        assertEquals(67, data.getPV2Power()); // [15]
+
+        assertEquals(49.96, data.getFrequencyPhase1()); // [16]
+        assertEquals(49.96, data.getFrequencyPhase2()); // [17]
+        assertEquals(49.96, data.getFrequencyPhase3()); // [18]
+
+        assertEquals(2, data.getInverterWorkModeCode()); // [19]
+        assertEquals("2", data.getInverterWorkMode()); // [19]
+
+        assertEquals(-41, data.getFeedInPower()); // [34] - [35]
+
+        assertEquals(313.3, data.getBatteryVoltage()); // [39]
+        assertEquals(3.2, data.getBatteryCurrent()); // [40]
+        assertEquals(1034, data.getBatteryPower()); // [41]
+        assertEquals(45, data.getBatteryLevel()); // [103]
+        assertEquals(59, data.getBatteryTemperature()); // [105]
+
+        // Totals
+        assertEquals(1294, data.getPowerUsage()); // [47]
+        assertEquals(50.5, data.getTotalEnergy()); // [68]
+        assertEquals(102, data.getTotalBatteryDischargeEnergy()); // [74]
+        assertEquals(142, data.getTotalBatteryChargeEnergy()); // [76]
+        assertEquals(57, data.getTotalPVEnergy()); // [80]
+        assertEquals(1925, data.getTotalFeedInEnergy()); // [86]
+        assertEquals(36.9, data.getTotalConsumption()); // [88]
+        assertEquals(46.3, data.getTodayEnergy()); // [82] / 10
+        assertEquals(5.06, data.getTodayFeedInEnergy()); // [90] / 100
+        assertEquals(3.04, data.getTodayConsumption()); // [92] / 100
+        assertEquals(6.2, data.getTodayBatteryDischargeEnergy()); // [78] / 100
+        assertEquals(11, data.getTodayBatteryChargeEnergy()); // [79] / 100
+    }
+
+    @Override
+    protected String getRawData() {
+        return RAW_DATA;
+    }
+}
diff --git a/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestX3MicOrProG2Parser.java b/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/local/parsers/TestX3MicOrProG2Parser.java
new file mode 100644 (file)
index 0000000..f983227
--- /dev/null
@@ -0,0 +1,97 @@
+/**
+ * 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.parsers;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+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.LocalData;
+import org.openhab.binding.solax.internal.model.local.RawDataParser;
+
+/**
+ * The {@link TestX3HybridG4Parser} simple test that tests for proper parsing against a real data from the inverter
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public class TestX3MicOrProG2Parser {
+
+    String rawData = """
+            {
+                sn:XYZ,
+                ver:3.003.02,
+                type:16,Data:[
+                    2515,2449,2484,5,5,9,54,44,20,4080,4340,
+                    0,1,2,0,67,102,0,4999,4999,4999,2,19035,
+                    0,50,8000,5,9,
+                    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+                    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,
+                    0,0,0,0,0,0,0,0,120,40,1,6,5,0,6772,0,0,0,
+                    0,0,0,0,0,0,0,0,0,0,0,0
+                    ],
+                Information:[8.000,16,XY,8,1.15,0.00,1.11,1.01,0.00,1]
+             }
+            """;
+
+    @Test
+    public void testParser() {
+        LocalConnectRawDataBean bean = LocalConnectRawDataBean.fromJson(rawData);
+        int type = bean.getType();
+        InverterType inverterType = InverterType.fromIndex(type);
+        assertEquals(InverterType.X3_MIC_OR_PRO_G2, inverterType, "Inverter type not recognized properly");
+
+        RawDataParser parser = inverterType.getParser();
+        assertNotNull(parser);
+
+        LocalData data = parser.getData(bean);
+        assertEquals("XYZ", data.getWifiSerial());
+        assertEquals("3.003.02", data.getWifiVersion());
+
+        assertEquals(251.5, data.getVoltagePhase1()); // [0]
+        assertEquals(244.9, data.getVoltagePhase2()); // [1]
+        assertEquals(248.4, data.getVoltagePhase3()); // [2]
+
+        assertEquals(0.5, data.getCurrentPhase1()); // [3]
+        assertEquals(0.5, data.getCurrentPhase2()); // [4]
+        assertEquals(0.9, data.getCurrentPhase3()); // [5]
+
+        assertEquals(54, data.getOutputPowerPhase1()); // [6]
+        assertEquals(44, data.getOutputPowerPhase2()); // [7]
+        assertEquals(20, data.getOutputPowerPhase3()); // [8]
+
+        assertEquals(408, data.getPV1Voltage()); // [9]
+        assertEquals(434, data.getPV2Voltage()); // [10]
+        assertEquals(0.1, data.getPV1Current()); // [12]
+        assertEquals(0.2, data.getPV2Current()); // [13]
+        assertEquals(67, data.getPV1Power()); // [15]
+        assertEquals(102, data.getPV2Power()); // [16]
+
+        assertEquals(49.99, data.getFrequencyPhase1()); // [18]
+        assertEquals(49.99, data.getFrequencyPhase2()); // [19]
+        assertEquals(49.99, data.getFrequencyPhase3()); // [20]
+
+        assertEquals(2, data.getInverterWorkModeCode()); // [21]
+        assertEquals("2", data.getInverterWorkMode()); // [21]
+
+        assertEquals(5, data.getInverterTemperature1()); // [26]
+        assertEquals(9, data.getInverterTemperature2()); // [27]
+
+        assertEquals(120, data.getTotalOutputPower()); // [78]
+
+        assertEquals(1903.5, data.getTotalEnergy()); // [22]
+        assertEquals(5.0, data.getTodayEnergy()); // [24]
+    }
+}
diff --git a/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/util/TestByteUtil.java b/bundles/org.openhab.binding.solax/src/test/java/org/openhab/binding/solax/internal/util/TestByteUtil.java
new file mode 100644 (file)
index 0000000..a66a4bd
--- /dev/null
@@ -0,0 +1,52 @@
+/**
+ * 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.util;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.Test;
+
+/**
+ * The {@link TestByteUtil} Simple test that tests the methods of the ByteUtil
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public class TestByteUtil {
+
+    @Test
+    public void testRead32BitSignedWithNegativeInputs() {
+        assertEquals(-65536, ByteUtil.read32BitSigned((short) 0, (short) -1));
+        assertEquals(-1, ByteUtil.read32BitSigned((short) -1, (short) -1));
+        assertEquals(-2, ByteUtil.read32BitSigned((short) -2, (short) -1));
+    }
+
+    @Test
+    public void testRead32BitSignedWithBoundaryValues() {
+        assertEquals(Short.MAX_VALUE, ByteUtil.read32BitSigned((short) Short.MAX_VALUE, (short) 0));
+        assertEquals((Short.MIN_VALUE & 0xFFFF) | (Short.MAX_VALUE << 16),
+                ByteUtil.read32BitSigned((short) Short.MIN_VALUE, (short) Short.MAX_VALUE));
+        assertEquals(Integer.MIN_VALUE, ByteUtil.read32BitSigned((short) 0, (short) (Integer.MIN_VALUE >> 16)));
+    }
+
+    @Test
+    public void testRead32BitSignedResultingInNegative() {
+        assertEquals(-131072, ByteUtil.read32BitSigned((short) 0, (short) -2));
+    }
+
+    @Test
+    public void testRead32BitSignedResultingInMaxInteger() {
+        assertEquals(Integer.MAX_VALUE, ByteUtil.read32BitSigned((short) 0xFFFF, (short) 0x7FFF));
+    }
+}