diff --git a/CODEOWNERS b/CODEOWNERS
index b433f9f1bda94..2a39f5c6a570d 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -324,6 +324,7 @@
/bundles/org.openhab.binding.solarlog/ @johannrichard
/bundles/org.openhab.binding.solarmax/ @jamietownsend
/bundles/org.openhab.binding.solarwatt/ @sven-carstens
+/bundles/org.openhab.binding.solax/ @theater
/bundles/org.openhab.binding.somfymylink/ @loungeflyz
/bundles/org.openhab.binding.somfytahoma/ @octa22
/bundles/org.openhab.binding.somneo/ @0x4d4d
diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml
index 024a15561867a..95eba8ac828c2 100644
--- a/bom/openhab-addons/pom.xml
+++ b/bom/openhab-addons/pom.xml
@@ -1606,6 +1606,11 @@
org.openhab.binding.solarwatt${project.version}
+
+ org.openhab.addons.bundles
+ org.openhab.binding.solax
+ ${project.version}
+ org.openhab.addons.bundlesorg.openhab.binding.somfymylink
diff --git a/bundles/org.openhab.binding.solax/NOTICE b/bundles/org.openhab.binding.solax/NOTICE
new file mode 100644
index 0000000000000..38d625e349232
--- /dev/null
+++ b/bundles/org.openhab.binding.solax/NOTICE
@@ -0,0 +1,13 @@
+This content is produced and maintained by the openHAB project.
+
+* Project home: https://www.openhab.org
+
+== Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License 2.0 which is available at
+https://www.eclipse.org/legal/epl-2.0/.
+
+== Source Code
+
+https://github.com/openhab/openhab-addons
diff --git a/bundles/org.openhab.binding.solax/README.md b/bundles/org.openhab.binding.solax/README.md
new file mode 100644
index 0000000000000..6b35fb62ea100
--- /dev/null
+++ b/bundles/org.openhab.binding.solax/README.md
@@ -0,0 +1,189 @@
+# Solax Binding
+
+This is a binding for Solax solar power inverters.
+
+Solax Wi-Fi module with direct connection via HTTP is supported.
+Wi-Fi module firmware version 3.x+ is required.
+Please note that earlier firmware releases do not support direct connection, therefore the binding will not work in its current state.
+
+The binding retrieves a structured data from the inverter's Wi-Fi module, parses it and pushes it into the inverter Thing where each channel represents a specific information (inverter output power, voltage, PV1 power, etc.)
+
+In case the parsed information that comes with the binding out of the box differs, the raw data channel can be used with a combination of JSON Path transformation to map the proper values to the necessary items.
+
+## Supported Things
+
+| Thing | Thing Type | Description |
+|------------------------|------------|-------------------------------------------------------------------------------------|
+| local-connect-inverter | Thing | This is model representation of inverter with all the data available as a channels |
+
+## Thing Configuration
+
+### Local Connect Inverter Configuration
+
+| 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. |
+
+### Inverter Output Channels
+
+| Channel | Type | Description |
+|--------------------------|----------------------------|--------------------------------------------------|
+| inverter-output-power | Number:Power | The output power of the inverter [W] |
+| inverter-current | Number:ElectricCurrent | The output current of the inverter [A] |
+| inverter-voltage | Number:ElectricPotential | The output voltage of the inverter [V] |
+| inverter-frequency | Number:Frequency | The frequency of the output voltage [Hz] |
+
+### Photovoltaic Panels Production Channels
+
+| Channel | Type | Description |
+|--------------------------|----------------------------|-------------------------------------------------|
+| pv1-voltage | Number:ElectricPotential | The voltage of PV1 string [V] |
+| pv2-voltage | Number:ElectricPotential | The voltage of PV2 string [V] |
+| pv1-current | Number:ElectricCurrent | The current of PV1 string [A] |
+| pv2-current | Number:ElectricCurrent | The current of PV2 string [A] |
+| pv1-power | Number:Power | The output power PV1 string [W] |
+| pv2-power | Number:Power | The output power PV2 string [W] |
+| pv-total-power | Number:Power | The total output power of both PV strings [W] |
+| pv-total-current | Number:ElectricCurrent | The total current of both PV strings [A] |
+
+### Battery channels
+
+| Channel | Type | Description |
+|---------------------------|----------------------------|------------------------------------------------------------------------------------------------|
+| battery-power | Number:Power | The power to / from battery (negative means power is pulled from battery and vice-versa) [W] |
+| battery-current | Number:ElectricCurrent | The current to / from battery (negative means power is pulled from battery and vice-versa) [A] |
+| battery-voltage | Number:ElectricPotential | The voltage of the battery [V] |
+| battery-temperature | Number:Temperature | The temperature of the battery [C/F] |
+| battery-state-of-charge | Number | The state of charge of the battery [%] |
+
+### Grid related channels
+
+| Channel | Type | Description |
+|--------------------------|----------------------------|------------------------------------------------------------------------------------------------|
+| feed-in-power | Number:Power | The power to / from grid (negative means power is pulled from the grid and vice-versa) [W] |
+
+### General channels
+
+| Channel | Type | Description |
+|--------------------------|----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------|
+| last-update-time | DateTime | Last time when a call has been made to the inverter |
+| raw-data | String | The raw data retrieved from inverter in JSON format. (Usable for channels not implemented. Can be consumed with the JSONpath transformation |
+
+### Properties
+
+| Property | Description |
+|-------------------|-------------------------------------------|
+| serialNumber | The serial number of the Wi-Fi module |
+| inverterType | Inverter Type (for example X1_HYBRID_G4) |
+
+## Full Example
+
+Here are some file based examples.
+
+### Thing Configuration
+
+```java
+// The local connect inverter thing
+Thing solax:local-connect-inverter:localInverter [ refreshInterval=10, password="", hostname="" ]
+```
+
+### Item Configuration
+
+```java
+Group gSolaxInverter "Solax Inverter" (boilerRoom)
+Group solarPanels "Solar panels" (gSolaxInverter)
+
+Number solaxPowerWest "West [%.0f W]" (gsolax_inverter,EveryChangePersist,solarPanels){ channel="solax:localConnectInverter:localInverter:pv1-power" }
+Number solaxPowerEast "East [%.0f W]" (gsolax_inverter,EveryChangePersist,solarPanels){ channel="solax:localConnectInverter:localInverter:pv2-power" }
+Number solaxBatteryPower "Battery power [%.0f W]" (gsolax_inverter,EveryChangePersist) { channel="solax:localConnectInverter:localInverter:battery-power" }
+Number solaxBatterySoc "Battery SoC [%.0f %%]" (gsolax_inverter,EveryChangePersist) { channel="solax:localConnectInverter:localInverter:battery-state-of-charge" }
+
+Number solaxFeedInPower "Feed-in power (CEZ) [%.0f W]" (gsolax_inverter,EveryChangePersist) { channel="solax:localConnectInverter:localInverter:feed-in-power" }
+Number solaxAcPower "Invertor output power [%.0f W]" (gsolax_inverter,EveryChangePersist){ channel="solax:localConnectInverter:localInverter:inverter-output-power" }
+
+String solaxInverterType "Inverter Type [%s]" (gsolax_inverter) { channel="solax:localConnectInverter:localInverter:inverter-type"}
+String solaxUploadTime "Last update time [%s]" (gsolax_inverter) { channel="solax:localConnectInverter:localInverter:last-update-time" }
+String solaxRawData "Raw data [%s]" (gsolax_inverter) { channel="solax:localConnectInverter:localInverter:raw-data" }
+```
+
+### Sitemap Configuration
+
+```perl
+Frame label="Solar power strings" {
+ Text item=solaxPowerEast valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"] {
+ Text item=solaxPowerEast icon="energy" valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"]
+ Text item=solaxPowerWest icon="energy" valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"]
+ Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"]
+ Chart item=solarPanels period=h refresh=600 visibility=[Chart_Period==0]
+ Chart item=solarPanels period=D refresh=3600 visibility=[Chart_Period==1]
+ Chart item=solarPanels period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
+ Chart item=solarPanels period=M refresh=3600 visibility=[Chart_Period==3]
+ Chart item=solarPanels period=Y refresh=3600 visibility=[Chart_Period==4]
+ }
+ Text item=solaxPowerWest valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"] {
+ Text item=solaxPowerEast icon="energy" valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"]
+ Text item=solaxPowerWest icon="energy" valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"]
+ Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"]
+ Chart item=solarPanels period=h refresh=600 visibility=[Chart_Period==0]
+ Chart item=solarPanels period=D refresh=3600 visibility=[Chart_Period==1]
+ Chart item=solarPanels period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
+ Chart item=solarPanels period=M refresh=3600 visibility=[Chart_Period==3]
+ Chart item=solarPanels period=Y refresh=3600 visibility=[Chart_Period==4]
+ }
+ Text item=solaxGenerationTotal valuecolor=[<=100="gray",<=500="red", <2000="orange", >=2000="green"] {
+ Text item=solaxPowerEast icon="energy" valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"]
+ Text item=solaxPowerWest icon="energy" valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"]
+ Text item=solaxGenerationTotal icon="energy" valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"]
+ Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"]
+ Chart item=solarPanels period=h refresh=600 visibility=[Chart_Period==0]
+ Chart item=solarPanels period=D refresh=3600 visibility=[Chart_Period==1]
+ Chart item=solarPanels period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
+ Chart item=solarPanels period=M refresh=3600 visibility=[Chart_Period==3]
+ Chart item=solarPanels period=Y refresh=3600 visibility=[Chart_Period==4]
+ }
+}
+Frame label="Consumption" {
+ Text item=solaxAcPower valuecolor=[<=30="gray", <800="green", <1500="orange", >=1500="red"] {
+ Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"]
+ Text item=solaxAcPower icon="energy" valuecolor=[<=30="gray", <800="green", <1500="orange", >=1500="red"]
+ Chart item=solaxAcPower period=h refresh=600 visibility=[Chart_Period==0]
+ Chart item=solaxAcPower period=D refresh=3600 visibility=[Chart_Period==1]
+ Chart item=solaxAcPower period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
+ Chart item=solaxAcPower period=M refresh=3600 visibility=[Chart_Period==3]
+ Chart item=solaxAcPower period=Y refresh=3600 visibility=[Chart_Period==4]
+ }
+ Text item=solaxFeedInPower valuecolor=[<=30="gray", <800="green", <1500="orange", >=1500="red"] {
+ Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"]
+ Text item=solaxFeedInPower icon="energy" valuecolor=[<=30="gray", <800="green", <1500="orange", >=1500="red"]
+ Chart item=solaxFeedInPower period=h refresh=600 visibility=[Chart_Period==0]
+ Chart item=solaxFeedInPower period=D refresh=3600 visibility=[Chart_Period==1]
+ Chart item=solaxFeedInPower period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
+ Chart item=solaxFeedInPower period=M refresh=3600 visibility=[Chart_Period==3]
+ Chart item=solaxFeedInPower period=Y refresh=3600 visibility=[Chart_Period==4]
+ }
+}
+Frame label="Battery" {
+ Text item=solaxBatteryPower valuecolor=[<=-500="red", <0="orange", ==0="gray", >0="green"] {
+ Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"]
+ Text item=solaxBatteryPower icon="energy" valuecolor=[<-800="red", <0="orange", ==0="gray", >=0="green"]
+ Chart item=solaxBatteryPower period=h refresh=600 visibility=[Chart_Period==0]
+ Chart item=solaxBatteryPower period=D refresh=3600 visibility=[Chart_Period==1]
+ Chart item=solaxBatteryPower period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
+ Chart item=solaxBatteryPower period=M refresh=3600 visibility=[Chart_Period==3]
+ Chart item=solaxBatteryPower period=Y refresh=3600 visibility=[Chart_Period==4]
+ }
+ Text item=solaxBatterySoc valuecolor=[<=30="red", <50="orange", >=50="green"] {
+ Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"]
+ Text item=solaxBatterySoc valuecolor=[<=30="red", <50="orange", >=50="green"]
+ Chart item=solaxBatterySoc period=h refresh=600 visibility=[Chart_Period==0]
+ Chart item=solaxBatterySoc period=D refresh=3600 visibility=[Chart_Period==1]
+ Chart item=solaxBatterySoc period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
+ Chart item=solaxBatterySoc period=M refresh=3600 visibility=[Chart_Period==3]
+ Chart item=solaxBatterySoc period=Y refresh=3600 visibility=[Chart_Period==4]
+ }
+}
+```
+
+
diff --git a/bundles/org.openhab.binding.solax/pom.xml b/bundles/org.openhab.binding.solax/pom.xml
new file mode 100644
index 0000000000000..6e3d15e5973f0
--- /dev/null
+++ b/bundles/org.openhab.binding.solax/pom.xml
@@ -0,0 +1,17 @@
+
+
+
+ 4.0.0
+
+
+ org.openhab.addons.bundles
+ org.openhab.addons.reactor.bundles
+ 4.1.0-SNAPSHOT
+
+
+ org.openhab.binding.solax
+
+ openHAB Add-ons :: Bundles :: Solax Binding
+
+
diff --git a/bundles/org.openhab.binding.solax/src/main/feature/feature.xml b/bundles/org.openhab.binding.solax/src/main/feature/feature.xml
new file mode 100644
index 0000000000000..ae1b59e819e82
--- /dev/null
+++ b/bundles/org.openhab.binding.solax/src/main/feature/feature.xml
@@ -0,0 +1,9 @@
+
+
+ mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features
+
+
+ openhab-runtime-base
+ mvn:org.openhab.addons.bundles/org.openhab.binding.solax/${project.version}
+
+
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxBindingConstants.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxBindingConstants.java
new file mode 100644
index 0000000000000..74151b3aecbc7
--- /dev/null
+++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxBindingConstants.java
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2010-2023 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;
+
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.thing.ThingTypeUID;
+
+/**
+ * The {@link SolaxBindingConstants} class defines common constants, which are
+ * used across the whole binding.
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public class SolaxBindingConstants {
+
+ private static final String BINDING_ID = "solax";
+ private static final String THING_LOCAL_CONNECT_INVERTER_ID = "local-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 Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_LOCAL_CONNECT_INVERTER);
+
+ // List of properties
+ public static final String PROPERTY_INVERTER_TYPE = "inverterType";
+
+ // List of all Channel ids
+ public static final String INVERTER_OUTPUT_POWER = "inverter-output-power";
+ public static final String INVERTER_OUTPUT_CURRENT = "inverter-current";
+ public static final String INVERTER_OUTPUT_VOLTAGE = "inverter-voltage";
+ public static final String INVERTER_OUTPUT_FREQUENCY = "inverter-frequency";
+
+ public static final String INVERTER_PV1_POWER = "pv1-power";
+ public static final String INVERTER_PV1_VOLTAGE = "pv1-voltage";
+ public static final String INVERTER_PV1_CURRENT = "pv1-current";
+
+ public static final String INVERTER_PV2_POWER = "pv2-power";
+ public static final String INVERTER_PV2_VOLTAGE = "pv2-voltage";
+ public static final String INVERTER_PV2_CURRENT = "pv2-current";
+
+ public static final String INVERTER_PV_TOTAL_POWER = "pv-total-power";
+ public static final String INVERTER_PV_TOTAL_CURRENT = "pv-total-current";
+
+ public static final String BATTERY_POWER = "battery-power";
+ public static final String BATTERY_VOLTAGE = "battery-voltage";
+ public static final String BATTERY_CURRENT = "battery-current";
+ public static final String BATTERY_TEMPERATURE = "battery-temperature";
+ public static final String BATTERY_STATE_OF_CHARGE = "battery-level";
+
+ public static final String FEED_IN_POWER = "feed-in-power";
+
+ public static final String TIMESTAMP = "last-update-time";
+ public static final String RAW_DATA = "raw-data";
+
+ // I18N Keys
+ protected static final String I18N_KEY_OFFLINE_COMMUNICATION_ERROR_JSON_CANNOT_BE_RETRIEVED = "@text/offline.communication-error.json-cannot-be-retrieved";
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxConfiguration.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxConfiguration.java
new file mode 100644
index 0000000000000..6114f746ea309
--- /dev/null
+++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxConfiguration.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2010-2023 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;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link SolaxConfiguration} class contains fields mapping thing configuration parameters.
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public class SolaxConfiguration {
+
+ public String hostname = "";
+ public String password = "";
+ public int refreshInterval = 10;
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxHandlerFactory.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxHandlerFactory.java
new file mode 100644
index 0000000000000..12c67ec6dd8a1
--- /dev/null
+++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxHandlerFactory.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2010-2023 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;
+
+import static org.openhab.binding.solax.internal.SolaxBindingConstants.*;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.binding.BaseThingHandlerFactory;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.openhab.core.thing.binding.ThingHandlerFactory;
+import org.osgi.service.component.annotations.Component;
+
+/**
+ * The {@link SolaxHandlerFactory} is responsible for creating things and thing
+ * handlers.
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+@Component(configurationPid = "binding.solax", service = ThingHandlerFactory.class)
+public class SolaxHandlerFactory extends BaseThingHandlerFactory {
+
+ @Override
+ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
+ return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
+ }
+
+ @Override
+ protected @Nullable ThingHandler createHandler(Thing thing) {
+ ThingTypeUID thingTypeUID = thing.getThingTypeUID();
+ if (THING_TYPE_LOCAL_CONNECT_INVERTER.equals(thingTypeUID)) {
+ return new SolaxLocalAccessHandler(thing);
+ }
+ return null;
+ }
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxLocalAccessHandler.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxLocalAccessHandler.java
new file mode 100644
index 0000000000000..7edcfaf216cf2
--- /dev/null
+++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/SolaxLocalAccessHandler.java
@@ -0,0 +1,171 @@
+/**
+ * Copyright (c) 2010-2023 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;
+
+import java.io.IOException;
+import java.time.ZonedDateTime;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.solax.internal.connectivity.LocalHttpConnector;
+import org.openhab.binding.solax.internal.connectivity.rawdata.LocalConnectRawDataBean;
+import org.openhab.binding.solax.internal.model.InverterData;
+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.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.binding.BaseThingHandler;
+import org.openhab.core.types.Command;
+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 BaseThingHandler {
+
+ private final Logger logger = LoggerFactory.getLogger(SolaxLocalAccessHandler.class);
+
+ private static final int INITIAL_SCHEDULE_DELAY_SECONDS = 5;
+
+ private @NonNullByDefault({}) LocalHttpConnector localHttpConnector;
+
+ private @Nullable ScheduledFuture> schedule;
+
+ public SolaxLocalAccessHandler(Thing thing) {
+ super(thing);
+ }
+
+ @Override
+ public void initialize() {
+ updateStatus(ThingStatus.UNKNOWN);
+
+ SolaxConfiguration config = getConfigAs(SolaxConfiguration.class);
+ localHttpConnector = new LocalHttpConnector(config.password, config.hostname);
+ int refreshInterval = config.refreshInterval;
+ TimeUnit timeUnit = TimeUnit.SECONDS;
+
+ logger.debug("Scheduling regular interval retrieval every {} {}", refreshInterval, timeUnit);
+ schedule = scheduler.scheduleWithFixedDelay(this::retrieveData, INITIAL_SCHEDULE_DELAY_SECONDS, refreshInterval,
+ timeUnit);
+ }
+
+ private void retrieveData() {
+ try {
+ String rawJsonData = localHttpConnector.retrieveData();
+ logger.debug("Raw data retrieved = {}", rawJsonData);
+
+ if (rawJsonData != null && !rawJsonData.isEmpty()) {
+ updateData(rawJsonData);
+ } else {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+ SolaxBindingConstants.I18N_KEY_OFFLINE_COMMUNICATION_ERROR_JSON_CANNOT_BE_RETRIEVED);
+ }
+ } catch (IOException e) {
+ logger.debug("Exception received while attempting to retrieve data via HTTP", e);
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
+ }
+ }
+
+ private void updateData(String rawJsonData) {
+ try {
+ LocalConnectRawDataBean inverterParsedData = parseJson(rawJsonData);
+ updateThing(inverterParsedData);
+ } catch (JsonParseException e) {
+ logger.debug("Unable to deserialize from JSON.", e);
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
+ }
+ }
+
+ private void updateThing(LocalConnectRawDataBean inverterParsedData) {
+ transferInverterDataToChannels(inverterParsedData);
+
+ if (getThing().getStatus() != ThingStatus.ONLINE) {
+ updateStatus(ThingStatus.ONLINE);
+ }
+ }
+
+ private LocalConnectRawDataBean parseJson(String rawJsonData) {
+ LocalConnectRawDataBean inverterParsedData = LocalConnectRawDataBean.fromJson(rawJsonData);
+ logger.debug("Received a new inverter data object. Data = {}", inverterParsedData.toStringDetailed());
+ return inverterParsedData;
+ }
+
+ private void transferInverterDataToChannels(InverterData data) {
+ updateProperty(Thing.PROPERTY_SERIAL_NUMBER, data.getWifiSerial());
+ updateProperty(SolaxBindingConstants.PROPERTY_INVERTER_TYPE, data.getInverterType().name());
+
+ updateState(SolaxBindingConstants.INVERTER_OUTPUT_POWER,
+ new QuantityType<>(data.getInverterOutputPower(), Units.WATT));
+ updateState(SolaxBindingConstants.INVERTER_OUTPUT_CURRENT,
+ new QuantityType<>(data.getInverterCurrent(), Units.AMPERE));
+ updateState(SolaxBindingConstants.INVERTER_OUTPUT_VOLTAGE,
+ new QuantityType<>(data.getInverterVoltage(), Units.VOLT));
+ updateState(SolaxBindingConstants.INVERTER_OUTPUT_FREQUENCY,
+ new QuantityType<>(data.getInverterFrequency(), Units.HERTZ));
+
+ updateState(SolaxBindingConstants.INVERTER_PV1_POWER, new QuantityType<>(data.getPV1Power(), Units.WATT));
+ updateState(SolaxBindingConstants.INVERTER_PV1_CURRENT, new QuantityType<>(data.getPV1Current(), Units.AMPERE));
+ updateState(SolaxBindingConstants.INVERTER_PV1_VOLTAGE, new QuantityType<>(data.getPV1Voltage(), Units.VOLT));
+
+ updateState(SolaxBindingConstants.INVERTER_PV2_POWER, new QuantityType<>(data.getPV2Power(), Units.WATT));
+ updateState(SolaxBindingConstants.INVERTER_PV2_CURRENT, new QuantityType<>(data.getPV2Current(), Units.AMPERE));
+ updateState(SolaxBindingConstants.INVERTER_PV2_VOLTAGE, new QuantityType<>(data.getPV2Voltage(), Units.VOLT));
+
+ updateState(SolaxBindingConstants.INVERTER_PV_TOTAL_POWER,
+ new QuantityType<>(data.getPVTotalPower(), Units.WATT));
+ updateState(SolaxBindingConstants.INVERTER_PV_TOTAL_CURRENT,
+ new QuantityType<>(data.getPVTotalCurrent(), Units.AMPERE));
+
+ updateState(SolaxBindingConstants.BATTERY_POWER, new QuantityType<>(data.getBatteryPower(), Units.WATT));
+ updateState(SolaxBindingConstants.BATTERY_CURRENT, new QuantityType<>(data.getBatteryCurrent(), Units.AMPERE));
+ updateState(SolaxBindingConstants.BATTERY_VOLTAGE, new QuantityType<>(data.getBatteryVoltage(), Units.VOLT));
+ updateState(SolaxBindingConstants.BATTERY_TEMPERATURE,
+ new QuantityType<>(data.getBatteryTemperature(), SIUnits.CELSIUS));
+ updateState(SolaxBindingConstants.BATTERY_STATE_OF_CHARGE,
+ new QuantityType<>(data.getBatterySoC(), Units.PERCENT));
+
+ updateState(SolaxBindingConstants.FEED_IN_POWER, new QuantityType<>(data.getFeedInPower(), Units.WATT));
+
+ updateState(SolaxBindingConstants.TIMESTAMP, new DateTimeType(ZonedDateTime.now()));
+ updateState(SolaxBindingConstants.RAW_DATA, new StringType(data.getRawData()));
+ }
+
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+ // Nothing to do here as of now. Maybe implement a REFRESH command in the future.
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+ ScheduledFuture> schedule = this.schedule;
+ if (schedule != null) {
+ schedule.cancel(true);
+ this.schedule = null;
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/LocalHttpConnector.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/LocalHttpConnector.java
new file mode 100644
index 0000000000000..ab1525b714617
--- /dev/null
+++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/LocalHttpConnector.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright (c) 2010-2023 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.connectivity;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.http.HttpMethod;
+import org.openhab.core.io.net.http.HttpUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link LocalHttpConnector} class uses HttpUtil to retrieve the raw JSON data from Inverter's Wi-Fi module.
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public class LocalHttpConnector implements SolaxConnector {
+
+ private static final int HTTP_REQUEST_TIME_OUT = 5000;
+
+ private static final String CONTENT_TYPE = "text/html; charset=utf-8";
+
+ private final Logger logger = LoggerFactory.getLogger(LocalHttpConnector.class);
+
+ private static final String OPT_TYPE = "optType";
+ private static final String READ_REALTIME_DATA = "ReadRealTimeData";
+
+ private static final String PASSWORD = "pwd";
+ // The serial number of the Wifi dongle is the password for the connection (default)
+ private String passwordValue;
+ private String uri;
+
+ public LocalHttpConnector(String passwordValue, String host) {
+ this.passwordValue = passwordValue;
+ this.uri = "http://" + host;
+ }
+
+ @Override
+ public @Nullable String retrieveData() throws IOException {
+ String requestBody = createRequestBody();
+ logger.trace("Uri: {}, Request body: {}", uri, requestBody);
+ String result = HttpUtil.executeUrl(HttpMethod.POST.name(), uri,
+ new ByteArrayInputStream(requestBody.getBytes(StandardCharsets.UTF_8)), CONTENT_TYPE,
+ HTTP_REQUEST_TIME_OUT);
+ logger.trace("Retrieved content = {}", result);
+ return result;
+ }
+
+ private String createRequestBody() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append(OPT_TYPE).append("=").append(READ_REALTIME_DATA);
+ sb.append("&");
+ sb.append(PASSWORD).append("=").append(passwordValue);
+
+ return sb.toString();
+ }
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/SolaxConnector.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/SolaxConnector.java
new file mode 100644
index 0000000000000..25eda68ae266b
--- /dev/null
+++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/SolaxConnector.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2010-2023 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.connectivity;
+
+import java.io.IOException;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * The {@link SolaxConnector} is interface for connecting to the Solax endpoints (cloud API or local IP)
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public interface SolaxConnector {
+
+ @Nullable
+ String retrieveData() throws IOException;
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/rawdata/LocalConnectRawDataBean.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/rawdata/LocalConnectRawDataBean.java
new file mode 100644
index 0000000000000..b6c978418ef35
--- /dev/null
+++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/rawdata/LocalConnectRawDataBean.java
@@ -0,0 +1,255 @@
+/**
+ * Copyright (c) 2010-2023 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.connectivity.rawdata;
+
+import java.util.Arrays;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.solax.internal.model.InverterData;
+import org.openhab.binding.solax.internal.model.InverterType;
+import org.openhab.binding.solax.internal.util.GsonSupplier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link LocalConnectRawDataBean} collects the raw data and the specific implementation to return the parsed data.
+ * If there are differences between the inverters probably would be wise to split the parsing in seprate class(es)
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public class LocalConnectRawDataBean implements RawDataBean, InverterData {
+
+ private final Logger logger = LoggerFactory.getLogger(LocalConnectRawDataBean.class);
+
+ private @Nullable String sn;
+ private @Nullable String ver;
+ private int type;
+ @SerializedName("Data")
+ private short @Nullable [] data;
+ @SerializedName("Information")
+ private String @Nullable [] information;
+ private @Nullable String rawData;
+
+ @Override
+ public String toString() {
+ return "LocalConnectRawDataBean [sn=" + sn + ", ver=" + ver + ", type=" + type + ", Information="
+ + Arrays.toString(information) + ", Data=" + Arrays.toString(data) + "]";
+ }
+
+ public @Nullable String getSn() {
+ return sn;
+ }
+
+ public void setSn(@Nullable String sn) {
+ this.sn = sn;
+ }
+
+ public @Nullable String getVer() {
+ return ver;
+ }
+
+ public void setVer(@Nullable String ver) {
+ this.ver = ver;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public void setType(int type) {
+ this.type = type;
+ }
+
+ public short @Nullable [] getData() {
+ return data;
+ }
+
+ public void setData(short @Nullable [] data) {
+ this.data = data;
+ }
+
+ public String @Nullable [] getInformation() {
+ return information;
+ }
+
+ public void setInformation(String @Nullable [] information) {
+ this.information = information;
+ }
+
+ @Override
+ public @Nullable String getRawData() {
+ return rawData;
+ }
+
+ public void setRawData(String rawData) {
+ this.rawData = rawData;
+ }
+
+ public static LocalConnectRawDataBean fromJson(String json) {
+ if (json.isEmpty()) {
+ throw new IllegalArgumentException("JSON payload should not be empty");
+ }
+
+ Gson gson = GsonSupplier.getInstance();
+ LocalConnectRawDataBean deserializedObject = gson.fromJson(json, LocalConnectRawDataBean.class);
+ if (deserializedObject == null) {
+ throw new IllegalStateException("Unexpected null result when deserializing JSON");
+ }
+ deserializedObject.setRawData(json);
+ return deserializedObject;
+ }
+
+ // Parsed inverter data interface implementation starts here
+
+ @Override
+ public @Nullable String getWifiSerial() {
+ return getSn();
+ }
+
+ @Override
+ public @Nullable String getWifiVersion() {
+ return getVer();
+ }
+
+ @Override
+ public InverterType getInverterType() {
+ return InverterType.fromIndex(type);
+ }
+
+ @Override
+ public short getInverterVoltage() {
+ return (short) (getData(0) / 10);
+ }
+
+ @Override
+ public short getInverterCurrent() {
+ return (short) (getData(1) / 10);
+ }
+
+ @Override
+ public short getInverterOutputPower() {
+ return getData(2);
+ }
+
+ @Override
+ public short getInverterFrequency() {
+ return (short) (getData(3) / 100);
+ }
+
+ @Override
+ public short getPV1Voltage() {
+ return (short) (getData(4) / 10);
+ }
+
+ @Override
+ public short getPV1Current() {
+ return (short) (getData(6) / 10);
+ }
+
+ @Override
+ public short getPV1Power() {
+ return getData(8);
+ }
+
+ @Override
+ public short getPV2Voltage() {
+ return (short) (getData(5) / 10);
+ }
+
+ @Override
+ public short getPV2Current() {
+ return (short) (getData(7) / 10);
+ }
+
+ @Override
+ public short getPV2Power() {
+ return getData(9);
+ }
+
+ @Override
+ public short getBatteryVoltage() {
+ return (short) (getData(14) / 100);
+ }
+
+ @Override
+ public short getBatteryCurrent() {
+ return (short) (getData(15) / 100);
+ }
+
+ @Override
+ public short getBatteryPower() {
+ return getData(16);
+ }
+
+ @Override
+ public short getBatteryTemperature() {
+ return getData(17);
+ }
+
+ @Override
+ public short getBatterySoC() {
+ return getData(18);
+ }
+
+ @Override
+ public long getOnGridTotalYield() {
+ return packU16(11, 12) / 100;
+ }
+
+ @Override
+ public short getOnGridDailyYield() {
+ return (short) (getData(13) / 10);
+ }
+
+ @Override
+ public short getFeedInPower() {
+ return getData(32);
+ }
+
+ @Override
+ public long getTotalFeedInEnergy() {
+ return packU16(34, 35) / 100;
+ }
+
+ @Override
+ public long getTotalConsumption() {
+ return packU16(36, 37) / 100;
+ }
+
+ private short getData(int index) {
+ try {
+ short[] dataArray = data;
+ 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;
+ }
+
+ private long packU16(int indexMajor, int indexMinor) {
+ short major = getData(indexMajor);
+ short minor = getData(indexMinor);
+ if (major == 0) {
+ return minor;
+ }
+
+ return ((major << 16) & 0xFFFF0000) | minor & 0xFFFF;
+ }
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/rawdata/RawDataBean.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/rawdata/RawDataBean.java
new file mode 100644
index 0000000000000..5edcddf4824b1
--- /dev/null
+++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/connectivity/rawdata/RawDataBean.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2010-2023 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.connectivity.rawdata;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * The {@link RawDataBean} is interface which should be implemented by all types of raw information that is retrieved
+ * (the idea is to retrieve a raw data from a Solax inverter locally or their cloud API)
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public interface RawDataBean {
+ @Nullable
+ String getRawData();
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/InverterData.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/InverterData.java
new file mode 100644
index 0000000000000..64d8375ad4ec4
--- /dev/null
+++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/InverterData.java
@@ -0,0 +1,92 @@
+/**
+ * Copyright (c) 2010-2023 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;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.solax.internal.connectivity.rawdata.RawDataBean;
+
+/**
+ * The {@link InverterData} interface should implement the interface that returns the parsed data in human readable code
+ * and format.
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public interface InverterData extends RawDataBean {
+ @Nullable
+ String getWifiSerial();
+
+ @Nullable
+ String getWifiVersion();
+
+ InverterType getInverterType();
+
+ short getInverterVoltage();
+
+ short getInverterCurrent();
+
+ short getInverterOutputPower();
+
+ short getInverterFrequency();
+
+ short getPV1Voltage();
+
+ short getPV1Current();
+
+ short getPV1Power();
+
+ short getPV2Voltage();
+
+ short getPV2Current();
+
+ short getPV2Power();
+
+ default short getPVTotalPower() {
+ return (short) (getPV1Power() + getPV2Power());
+ }
+
+ default short getPVTotalCurrent() {
+ return (short) (getPV1Current() + getPV2Current());
+ }
+
+ short getBatteryVoltage(); // V / 100
+
+ short getBatteryCurrent(); // A / 100
+
+ short getBatteryPower(); // W
+
+ short getBatteryTemperature(); // temperature C
+
+ short getBatterySoC(); // % battery SoC
+
+ long getOnGridTotalYield(); // KWh total Yeld from the sun (to the grid?)
+
+ short getOnGridDailyYield(); // KWh daily Yeld from the sun (to the grid?)
+
+ long getTotalFeedInEnergy(); // KWh all times
+
+ long getTotalConsumption(); // KWh all times
+
+ short getFeedInPower();
+
+ default String toStringDetailed() {
+ return "WifiSerial = " + getWifiSerial() + ", WifiVersion = " + getWifiVersion() + ", InverterType = "
+ + getInverterType() + ", InverterVoltage = " + getInverterVoltage() + "V, InverterCurrent = "
+ + getInverterCurrent() + "A, InverterPower = " + getInverterOutputPower() + "W, BatteryPower = "
+ + getBatteryPower() + "W, Battery SoC = " + getBatterySoC() + "%, FeedIn Power = " + getFeedInPower()
+ + "W, Total PV Power = " + (getPV1Power() + getPV2Power()) + "W, Total Consumption = "
+ + getTotalConsumption() + "kWh, Total Feed-in Energy = " + getTotalFeedInEnergy()
+ + "kWh, Total On-Grid Yield = " + getOnGridTotalYield() + "kWh.";
+ }
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/InverterType.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/InverterType.java
new file mode 100644
index 0000000000000..b8131fb16baf0
--- /dev/null
+++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/model/InverterType.java
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2010-2023 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;
+
+import java.util.stream.Stream;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link InverterType} class is enum representing the different inverter types with a simple logic to convert from
+ * int(coming from the JSON) to a more meaningful enum value.
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public enum InverterType {
+
+ X1_LX(1),
+ X_HYBRID(2),
+ X1_HYBRID_FIT(3),
+ X1_BOOST_AIR_MINI(4),
+ X3_HYBRID_FIT(5),
+ X3_20K_30K(6),
+ X3_MIC_PRO(7),
+ X1_SMART(8),
+ X1_AC(9),
+ A1_HYBRID(10),
+ A1_FIT(11),
+ A1_GRID(12),
+ J1_ESS(13),
+ X3_HYBRID_G4(14),
+ X1_HYBRID_G4(15),
+ UNKNOWN(-1);
+
+ private int typeIndex;
+
+ InverterType(int typeIndex) {
+ this.typeIndex = typeIndex;
+ }
+
+ public static InverterType fromIndex(int index) {
+ InverterType[] values = InverterType.values();
+ return Stream.of(values).filter(value -> value.typeIndex == index).findFirst().orElse(UNKNOWN);
+ }
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/util/GsonSupplier.java b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/util/GsonSupplier.java
new file mode 100644
index 0000000000000..283a6ce59604b
--- /dev/null
+++ b/bundles/org.openhab.binding.solax/src/main/java/org/openhab/binding/solax/internal/util/GsonSupplier.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2010-2023 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;
+
+import com.google.gson.Gson;
+
+/**
+ * The {@link GsonSupplier} provides a singleton instance of a Gson object
+ *
+ * @author Konstantin Polihronov - Initial contribution
+ */
+@NonNullByDefault
+public class GsonSupplier {
+ private static final Gson GSON = new Gson();
+
+ private GsonSupplier() {
+ };
+
+ public static Gson getInstance() {
+ return GSON;
+ }
+}
diff --git a/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/addon/addon.xml
new file mode 100644
index 0000000000000..b455cf3b43f7b
--- /dev/null
+++ b/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/addon/addon.xml
@@ -0,0 +1,11 @@
+
+
+
+ binding
+ Solax Binding
+ This is the binding for Solax inverters.
+ local
+
+
diff --git a/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/i18n/solax.properties b/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/i18n/solax.properties
new file mode 100644
index 0000000000000..3b132065b4704
--- /dev/null
+++ b/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/i18n/solax.properties
@@ -0,0 +1,67 @@
+# add-on
+
+addon.solax.name = Solax Binding
+addon.solax.description = This is the binding for Solax inverters.
+
+# thing types
+
+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
+thing-type.solax.local-connect-inverter.channel.battery-current.description = Electric current to/from the battery
+thing-type.solax.local-connect-inverter.channel.battery-level.label = Battery Level
+thing-type.solax.local-connect-inverter.channel.battery-level.description = The battery state of charge in percent
+thing-type.solax.local-connect-inverter.channel.battery-power.label = Battery Power
+thing-type.solax.local-connect-inverter.channel.battery-power.description = Power to/from the battery
+thing-type.solax.local-connect-inverter.channel.battery-temperature.label = Battery Temperature
+thing-type.solax.local-connect-inverter.channel.battery-temperature.description = Temperature of the battery
+thing-type.solax.local-connect-inverter.channel.battery-voltage.label = Battery Voltage
+thing-type.solax.local-connect-inverter.channel.battery-voltage.description = Electric voltage of the battery
+thing-type.solax.local-connect-inverter.channel.feed-in-power.label = Feed-in Power
+thing-type.solax.local-connect-inverter.channel.feed-in-power.description = Power to/from the electricity network.
+thing-type.solax.local-connect-inverter.channel.inverter-current.label = Inverter Input/Output Current
+thing-type.solax.local-connect-inverter.channel.inverter-current.description = Current to/from the inverter
+thing-type.solax.local-connect-inverter.channel.inverter-output-power.label = Inverter Input/Output Power
+thing-type.solax.local-connect-inverter.channel.inverter-output-power.description = Power to/from the inverter
+thing-type.solax.local-connect-inverter.channel.inverter-voltage.label = Inverter Voltage
+thing-type.solax.local-connect-inverter.channel.inverter-voltage.description = Voltage of the inverter
+thing-type.solax.local-connect-inverter.channel.pv-total-current.label = PV Total Current
+thing-type.solax.local-connect-inverter.channel.pv-total-current.description = The sum of PV currents from all strings
+thing-type.solax.local-connect-inverter.channel.pv-total-power.label = PV Total Power
+thing-type.solax.local-connect-inverter.channel.pv-total-power.description = The sum of PV powers from all strings
+thing-type.solax.local-connect-inverter.channel.pv1-current.label = PV 1 Current
+thing-type.solax.local-connect-inverter.channel.pv1-current.description = Electric current of PV String 1
+thing-type.solax.local-connect-inverter.channel.pv1-power.label = PV 1 Power
+thing-type.solax.local-connect-inverter.channel.pv1-power.description = Electric power of PV String 1
+thing-type.solax.local-connect-inverter.channel.pv1-voltage.label = PV 1 Voltage
+thing-type.solax.local-connect-inverter.channel.pv1-voltage.description = Electric voltage of PV String 1
+thing-type.solax.local-connect-inverter.channel.pv2-current.label = PV 2 Current
+thing-type.solax.local-connect-inverter.channel.pv2-current.description = Electric current of PV String 2
+thing-type.solax.local-connect-inverter.channel.pv2-power.label = PV 2 Power
+thing-type.solax.local-connect-inverter.channel.pv2-power.description = Electric power of PV String 2
+thing-type.solax.local-connect-inverter.channel.pv2-voltage.label = PV 2 Voltage
+thing-type.solax.local-connect-inverter.channel.pv2-voltage.description = Electric voltage of PV String 2
+
+# thing types config
+
+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
+thing-type.config.solax.local-connect-inverter.password.description = Password for accessing the Wi-Fi module (the serial number of the Wi-Fi module)
+thing-type.config.solax.local-connect-inverter.refreshInterval.label = Refresh Interval
+thing-type.config.solax.local-connect-inverter.refreshInterval.description = Specifies the refresh interval in seconds.
+
+# channel types
+
+channel-type.solax.battery-temperature.label = Battery Temperature
+channel-type.solax.battery-temperature.description = Battery Temperature
+channel-type.solax.frequency.label = Electric Frequency
+channel-type.solax.frequency.description = Frequency of the electricity to/from the inverter
+channel-type.solax.last-retrieve-time-stamp.label = Last Retrieve Time Stamp
+channel-type.solax.last-retrieve-time-stamp.description = Last time with a successful retrieval of data
+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.
+
+# thing status descriptions
+
+offline.communication-error.json-cannot-be-retrieved = JSON data could not be retrieved.
diff --git a/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/thing/channel_types.xml b/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/thing/channel_types.xml
new file mode 100644
index 0000000000000..a1a814436d56e
--- /dev/null
+++ b/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/thing/channel_types.xml
@@ -0,0 +1,41 @@
+
+
+
+
+ Number:Frequency
+
+ Frequency of the electricity to/from the inverter
+
+ Measurement
+ Frequency
+
+
+
+
+ Number:Temperature
+
+ Battery Temperature
+
+ Measurement
+ Temperature
+
+
+
+
+ DateTime
+
+ Last time with a successful retrieval of data
+ Time
+
+
+
+ String
+
+ The raw JSON data retrieved from the inverter's Wi-Fi module.
+
+
+
+
diff --git a/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/thing/localConnectInverter.xml b/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/thing/localConnectInverter.xml
new file mode 100644
index 0000000000000..8a3c4f7982c48
--- /dev/null
+++ b/bundles/org.openhab.binding.solax/src/main/resources/OH-INF/thing/localConnectInverter.xml
@@ -0,0 +1,109 @@
+
+
+
+
+
+
+ The inverter representation that supports local connections via HTTP
+
+
+
+
+ Power to/from the inverter
+
+
+
+ Current to/from the inverter
+
+
+
+ Voltage of the inverter
+
+
+
+
+
+ Electric voltage of PV String 1
+
+
+
+ Electric voltage of PV String 2
+
+
+
+ Electric current of PV String 1
+
+
+
+ Electric current of PV String 2
+
+
+
+ Electric power of PV String 1
+
+
+
+ Electric power of PV String 2
+
+
+
+ The sum of PV powers from all strings
+
+
+
+ The sum of PV currents from all strings
+
+
+
+
+ Power to/from the battery
+
+
+
+ Electric current to/from the battery
+
+
+
+ Electric voltage of the battery
+
+
+
+ Temperature of the battery
+
+
+
+ The battery state of charge in percent
+
+
+
+
+ Power to/from the electricity network.
+
+
+
+
+
+
+
+
+
+
+ Specifies the refresh interval in seconds.
+ 10
+
+
+
+ Password for accessing the Wi-Fi module (the serial number of the Wi-Fi module)
+ password
+
+
+
+ IP address or the host name of the Wi-Fi module
+ network-address
+
+
+
+
diff --git a/bundles/pom.xml b/bundles/pom.xml
index dbaf85bd430fd..9c099d94e55fb 100644
--- a/bundles/pom.xml
+++ b/bundles/pom.xml
@@ -356,6 +356,7 @@
org.openhab.binding.solarlogorg.openhab.binding.solarmaxorg.openhab.binding.solarwatt
+ org.openhab.binding.solaxorg.openhab.binding.somfymylinkorg.openhab.binding.somfytahomaorg.openhab.binding.somneo