diff --git a/CODEOWNERS b/CODEOWNERS
index 0a4f724322f..085876b5408 100755
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -344,6 +344,7 @@
/bundles/org.openhab.binding.solaredge/ @alexf2015
/bundles/org.openhab.binding.solarforecast/ @weymann
/bundles/org.openhab.binding.solarlog/ @johannrichard
+/bundles/org.openhab.binding.solarman/ @catalinsanda
/bundles/org.openhab.binding.solarmax/ @jamietownsend
/bundles/org.openhab.binding.solarwatt/ @sven-carstens
/bundles/org.openhab.binding.solax/ @theater
diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml
index e95b1542ac8..e4acce9bd8d 100644
--- a/bom/openhab-addons/pom.xml
+++ b/bom/openhab-addons/pom.xml
@@ -1716,6 +1716,11 @@
org.openhab.binding.solarlog
${project.version}
+
+ org.openhab.addons.bundles
+ org.openhab.binding.solarman
+ ${project.version}
+
org.openhab.addons.bundles
org.openhab.binding.solarmax
diff --git a/bundles/org.openhab.binding.solarman/NOTICE b/bundles/org.openhab.binding.solarman/NOTICE
new file mode 100644
index 00000000000..38d625e3492
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/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.solarman/README.md b/bundles/org.openhab.binding.solarman/README.md
new file mode 100644
index 00000000000..7676fd28a9e
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/README.md
@@ -0,0 +1,290 @@
+# Solarman Logger Binding
+
+Binding used to communicate with Solarman (IGEN-Tech) v5 based solar inverter data loggers in direct-mode over the local network.
+More information about the different types of stick loggers is available on the [Solarman](https://www.solarmanpv.com/products/data-logger/stick-logger/) site.
+
+These data loggers are used by inverters from a lot of manufacturers, just to name a few:
+
+- Deye
+- Sofar
+- Solis
+- ZCS Azzurro
+- KStar
+
+## Supported Things
+
+The `solarman:logger` thing supports reading data from a Solarman LSW-3 Stick Logger (it might also work with LSE-3 and maybe others) when connected to a supported inverter.
+
+It was tested on a SUN-12K-SG04LP3-EU only, but because the implementation uses the inverter definitions created as part of Stephan Joubert's Home Assistant plugin it **might** work with the other inverters supported by the plugin.
+
+## Thing Configuration
+
+To connect the logger you need the IP address of the logger and its serial number.
+The IP address can be obtained from your router and the serial number can either be read from the label of the logger, or by connecting to the logger with a browser (default user/pass: admin/admin) and getting it from the Status page.
+*Please note* that you need the "Device serial number" from the "Device information" section, not the "Inverter serial number".
+
+### `logger` Thing Configuration
+
+| Name | Type | Description | Default | Required | Advanced |
+|--------------------|---------|--------------------------------------------------------|---------|----------|----------|
+| hostname | text | Hostname or IP address of the Solarman logger | N/A | yes | no |
+| serialNumber | text | Serial number of the Solarman logger | N/A | yes | no |
+| inverterType | text | The type of inverter connected to the logger | N/A | yes | no |
+| port | integer | Port of the Solarman logger | 8899 | no | yes |
+| refreshInterval | integer | Interval the device is polled in sec. | 60 | no | yes |
+| additionalRequests | text | Additional requests besides the ones in the definition | N/A | no | yes |
+
+The `inverterType` parameter governs what registers the binding will read from the logger and what channels it will expose.
+
+Possible values:
+
+| Inverter Type | Inverters supported | Notes |
+|--------------------|---------------------------------------------|------------------------------------------------------------------|
+| deye_hybrid | DEYE/Sunsynk/SolArk Hybrid inverters | used when no lookup specified |
+| deye_sg04lp3 | DEYE/Sunsynk/SolArk Hybrid 8/12K-SG04LP3 | e.g. 12K-SG04LP3-EU |
+| deye_string | DEYE/Sunsynk/SolArk String inverters | e.g. SUN-4/5/6/7/8/10/12K-G03 Plus |
+| deye_2mppt | DEYE Microinverter with 2 MPPT Trackers | e.g. SUN600G3-EU-230 / SUN800G3-EU-230 / SUN1000G3-EU-230 |
+| deye_4mppt | DEYE Microinverter with 4 MPPT Trackers | e.g. SUN1300G3-EU-230 / SUN1600G3-EU-230 / SUN2000G3-EU-230 |
+| sofar_lsw3 | SOFAR Inverters | |
+| sofar_g3hyd | SOFAR Hybrid Three-Phase inverter | HYD 6000 or rebranded (three-phase), ex. ZCS Azzurro 3PH HYD-ZSS |
+| sofar_hyd3k-6k-es | SOFAR Hybrid Single-Phase inverter | HYD 6000 or rebranded (single-phase), ex. ZCS Azzurro HYD-ZSS |
+| solis_hybrid | SOLIS Hybrid inverter | |
+| solid_1p8k-5g | SOLIS 1P8K-5G | |
+| zcs_azzurro-ktl-v3 | ZCS Azzurro KTL-V3 inverters | ZCS Azzurro 3.3/4.4/5.5/6.6 KTL-V3 (rebranded Sofar KTLX-G3) |
+
+The `additionalRequests` allows the user to specify additional address ranges to be polled. The format of the value is `mb_functioncode1:start1-end1, mb_functioncode2:start2-end2,...`
+For example `"0x03:0x27D-0x27E"` will issue an additional read for Holding Registers between `0x27D` and `0x27E`.
+
+This is useful when coupled with user defined channels, for example a thing definition like the one below will also read the register for the AC frequency on a Deye inverter, besides the ones pre-defined in the `deye_sg04lp3` inverter definition.
+
+```java
+Thing solarman:logger:local [ hostname="x.x.x.x", inverterType="deye_sg04lp3", serialNumber="1234567890", additionalRequests="0x03:0x27D-0x27E" ] {
+ Channels:
+ Type number : Inverter_Frequency [scale="0.01", uom="Hz", rule="3", registers="0x27E"]
+}
+```
+
+**Please note** As of this writing inverter types besides the `deye_sg04lp3` were not tested to work.
+If you have one of those inverters and it works, please drop me a message, if it doesn't work, please open an issue and I'll try to fix it.
+
+## Channels
+
+The list of channels is not static, it is generated dynamically based on the inverter type selected.
+
+This is the list you get for the `deye_sg04lp3` inverter type:
+
+| Channel | Type | Read/Write | Description |
+|------------------------------------------|--------|--------------|-------------------------------------------------------|
+| alert-alert | Number | R | Alert \[0x0229,0x022A,0x022B,0x022C,0x022D,0x022E\] |
+| battery-battery-current | Number | R | Battery Current \[0x024F\] |
+| battery-battery-power | Number | R | Battery Power \[0x024E\] |
+| battery-battery-soc | Number | R | Battery SOC \[0x024C\] |
+| battery-battery-temperature | Number | R | Battery Temperature \[0x024A\] |
+| battery-battery-voltage | Number | R | Battery Voltage \[0x024B\] |
+| battery-daily-battery-charge | Number | R | Daily Battery Charge \[0x0202\] |
+| battery-daily-battery-discharge | Number | R | Daily Battery Discharge \[0x0203\] |
+| battery-total-battery-charge | Number | R | Total Battery Charge \[0x0204,0x0205\] |
+| battery-total-battery-discharge | Number | R | Total Battery Discharge \[0x0206,0x0207\] |
+| grid-daily-energy-bought | Number | R | Daily Energy Bought \[0x0208\] |
+| grid-daily-energy-sold | Number | R | Daily Energy Sold \[0x0209\] |
+| grid-external-ct-l1-power | Number | R | External CT L1 Power \[0x0268\] |
+| grid-external-ct-l2-power | Number | R | External CT L2 Power \[0x0269\] |
+| grid-external-ct-l3-power | Number | R | External CT L3 Power \[0x026A\] |
+| grid-grid-voltage-l1 | Number | R | Grid Voltage L1 \[0x0256\] |
+| grid-grid-voltage-l2 | Number | R | Grid Voltage L2 \[0x0257\] |
+| grid-grid-voltage-l3 | Number | R | Grid Voltage L3 \[0x0258\] |
+| grid-internal-ct-l1-power | Number | R | Internal CT L1 Power \[0x025C\] |
+| grid-internal-ct-l2-power | Number | R | Internal CT L2 Power \[0x025D\] |
+| grid-internal-ct-l3-power | Number | R | Internal CT L3 Power \[0x025E\] |
+| grid-total-energy-bought | Number | R | Total Energy Bought \[0x020A,0x020B\] |
+| grid-total-energy-sold | Number | R | Total Energy Sold \[0x020C\] |
+| grid-total-grid-power | Number | R | Total Grid Power \[0x0271\] |
+| grid-total-grid-production | Number | R | Total Grid Production \[0x020C,0x020D\] |
+| inverter-ac-temperature | Number | R | AC Temperature \[0x021D\] |
+| inverter-communication-board-version-no- | Number | R | Communication Board Version No \[0x0011\] |
+| inverter-control-board-version-no- | Number | R | Control Board Version No \[0x000D\] |
+| inverter-current-l1 | Number | R | Current L1 \[0x0276\] |
+| inverter-current-l2 | Number | R | Current L2 \[0x0277\] |
+| inverter-current-l3 | Number | R | Current L3 \[0x0278\] |
+| inverter-dc-temperature | Number | R | DC Temperature \[0x021C\] |
+| inverter-frequency | Number | R | Number Value \[0x27E\] |
+| inverter-inverter-id | String | R | Inverter ID \[0x0003,0x0004,0x0005,0x0006,0x0007\] |
+| inverter-inverter-l1-power | Number | R | Inverter L1 Power \[0x0279\] |
+| inverter-inverter-l2-power | Number | R | Inverter L2 Power \[0x027A\] |
+| inverter-inverter-l3-power | Number | R | Inverter L3 Power \[0x027B\] |
+| solar-daily-production | Number | R | Daily Production \[0x0211\] |
+| solar-pv1-current | Number | R | PV1 Current \[0x02A5\] |
+| solar-pv1-power | Number | R | PV1 Power \[0x02A0\] |
+| solar-pv1-voltage | Number | R | PV1 Voltage \[0x02A4\] |
+| solar-pv2-current | Number | R | PV2 Current \[0x02A7\] |
+| solar-pv2-power | Number | R | PV2 Power \[0x02A1\] |
+| solar-pv2-voltage | Number | R | PV2 Voltage \[0x02A6\] |
+| solar-total-production | Number | R | Total Production \[0x0216,0x0217\] |
+| upload-daily-load-consumption | Number | R | Daily Load Consumption \[0x020E\] |
+| upload-load-l1-power | Number | R | Load L1 Power \[0x028A\] |
+| upload-load-l2-power | Number | R | Load L2 Power \[0x028B\] |
+| upload-load-l3-power | Number | R | Load L3 Power \[0x028C\] |
+| upload-load-voltage-l1 | Number | R | Load Voltage L1 \[0x0284\] |
+| upload-load-voltage-l2 | Number | R | Load Voltage L2 \[0x0285\] |
+| upload-load-voltage-l3 | Number | R | Load Voltage L3 \[0x0286\] |
+| upload-total-load-consumption | Number | R | Total Load Consumption \[0x020F,0x0210\] |
+| upload-total-load-power | Number | R | Total Load Power \[0x028D\] |
+
+## Full Example
+
+This is an example for a DEYE 12kW (SUN-12K-SG04LP3-EU) hybrid inverter
+
+### `solarman.things`
+
+Please replace the `hostname` and `serialNumber` with the correct values for your logger.
+
+```java
+Thing solarman:logger:local [hostname="x.x.x.x",inverterType="deye_sg04lp3",serialNumber="1234567890"]
+```
+
+### `solarman.items`
+
+Items file example for a SUN-12K-SG04LP3-EU inverter
+
+```text
+Number:Dimensionless Communication_Board_Version_No "Communication Board Version No [%s]" (solarman) {channel="solarman:logger:local:inverter-communication-board-version-no-"}
+Number:Dimensionless Control_Board_Version_No "Control Board Version No [%s]" (solarman) {channel="solarman:logger:local:inverter-control-board-version-no-"}
+String Inverter_Id "Inverter Id [%s]" (solarman) {channel="solarman:logger:local:inverter-inverter-id"}
+Number:Temperature AC_Temperature "AC Temperature [%.1f °C]" (solarman) {channel="solarman:logger:local:inverter-ac-temperature", unit="°C"}
+Number:Temperature DC_Temperature "DC Temperature [%.1f °C]" (solarman) {channel="solarman:logger:local:inverter-dc-temperature", unit="°C"}
+Number:Power Inverter_L1_Power "Inverter L1 Power [%d W]" (solarman) {channel="solarman:logger:local:inverter-inverter-l1-power", unit="W"}
+Number:Power Inverter_L2_Power "Inverter L2 Power [%d W]" (solarman) {channel="solarman:logger:local:inverter-inverter-l2-power", unit="W"}
+Number:Power Inverter_L3_Power "Inverter L3 Power [%d W]" (solarman) {channel="solarman:logger:local:inverter-inverter-l3-power", unit="W"}
+Number:ElectricCurrent Current_L1 "Current L1 [%.1f A]" (solarman) {channel="solarman:logger:local:inverter-current-l1", unit="A"}
+Number:ElectricCurrent Current_L2 "Current L2 [%.1f A]" (solarman) {channel="solarman:logger:local:inverter-current-l2", unit="A"}
+Number:ElectricCurrent Current_L3 "Current L3 [%.1f A]" (solarman) {channel="solarman:logger:local:inverter-current-l3", unit="A"}
+Number:Power External_CT_L1_Power "External CT L1 Power [%d W]" (solarman) {channel="solarman:logger:local:grid-external-ct-l1-power", unit="W"}
+Number:Power External_CT_L2_Power "External CT L2 Power [%d W]" (solarman) {channel="solarman:logger:local:grid-external-ct-l2-power", unit="W"}
+Number:Power External_CT_L3_Power "External CT L3 Power [%d W]" (solarman) {channel="solarman:logger:local:grid-external-ct-l3-power", unit="W"}
+Number:Power Internal_CT_L1_Power "Internal CT L1 Power [%d W]" (solarman) {channel="solarman:logger:local:grid-internal-ct-l1-power", unit="W"}
+Number:Power Internal_CT_L2_Power "Internal CT L2 Power [%d W]" (solarman) {channel="solarman:logger:local:grid-internal-ct-l2-power", unit="W"}
+Number:Power Internal_CT_L3_Power "Internal CT L3 Power [%d W]" (solarman) {channel="solarman:logger:local:grid-internal-ct-l3-power", unit="W"}
+Number:ElectricPotential Grid_Voltage_L1 "Grid Voltage L1 [%d V]" (solarman) {channel="solarman:logger:local:grid-grid-voltage-l1", unit="V"}
+Number:ElectricPotential Grid_Voltage_L2 "Grid Voltage L2 [%d V]" (solarman) {channel="solarman:logger:local:grid-grid-voltage-l2", unit="V"}
+Number:ElectricPotential Grid_Voltage_L3 "Grid Voltage L3 [%d V]" (solarman) {channel="solarman:logger:local:grid-grid-voltage-l3", unit="V"}
+Number:Power Total_Grid_Power "Total Instant Grid Power [%d W]" (solarman) {channel="solarman:logger:local:grid-total-grid-power", unit="W"}
+Number:Energy Total_Grid_Production "Total Grid Feed-in [%.1f kWh]" (solarman) {channel="solarman:logger:local:grid-total-grid-production", unit="kWh"}
+Number:Energy Daily_Energy_Sold "Daily Energy Sold [%d Wh]" (solarman) {channel="solarman:logger:local:grid-daily-energy-sold", unit="Wh"}
+Number:Energy Total_Energy_Sold "Total Energy Sold [%d kWh]" (solarman) {channel="solarman:logger:local:grid-total-energy-sold", unit="kWh"}
+Number:Energy Total_Energy_Bought "Total Energy Bought [%d kWh]" (solarman) {channel="solarman:logger:local:grid-total-energy-bought", unit="kWh"}
+Number:Energy Daily_Energy_Bought "Daily Energy Bought [%d kWh]" (solarman) {channel="solarman:logger:local:grid-daily-energy-bought", unit="kWh"}
+Number:Energy Daily_Production "Daily Production [%.1f kWh]" (solarman) {channel="solarman:logger:local:solar-daily-production", unit="kWh"}
+Number:Energy Total_Production "Total Production [%d kWh]" (solarman) {channel="solarman:logger:local:solar-total-production", unit="kWh"}
+Number:Energy Daily_Load_Consumption "Daily Load Consumption [%.1f kWh]" (solarman) {channel="solarman:logger:local:upload-daily-load-consumption", unit="kWh"}
+Number:Energy Total_Load_Consumption "Total Load Consumption [%d kWh]" (solarman) {channel="solarman:logger:local:upload-total-load-consumption", unit="kWh"}
+Number:Power Load_L1_Power "Load L1 Power [%d W]" (solarman) {channel="solarman:logger:local:upload-load-l1-power", unit="W"}
+Number:Power Load_L2_Power "Load L2 Power [%d W]" (solarman) {channel="solarman:logger:local:upload-load-l2-power", unit="W"}
+Number:Power Load_L3_Power "Load L3 Power [%d W]" (solarman) {channel="solarman:logger:local:upload-load-l3-power", unit="W"}
+Number:Power Total_Load_Power "Total Load Power [%d W]" (solarman) {channel="solarman:logger:local:upload-total-load-power", unit="W"}
+Number:ElectricPotential Load_Voltage_L1 "Load Voltage L1 [%d V]" (solarman) {channel="solarman:logger:local:upload-load-voltage-l1", unit="V"}
+Number:ElectricPotential Load_Voltage_L2 "Load Voltage L2 [%d V]" (solarman) {channel="solarman:logger:local:upload-load-voltage-l2", unit="V"}
+Number:ElectricPotential Load_Voltage_L3 "Load Voltage L3 [%d V]" (solarman) {channel="solarman:logger:local:upload-load-voltage-l3", unit="V"}
+Number:Energy Daily_Energy_Consumption "Daily Energy Consumption [%d kWh]" (solarman) {channel="solarman:logger:local:upload-daily-load-consumption", unit="kWh"}
+Number:Energy Total_Energy_Consumption "Total Energy Consumption [%d kWh]" (solarman) {channel="solarman:logger:local:upload-total-load-consumption", unit="kWh"}
+Number:ElectricCurrent PV1_Current "PV1 Current [%.1f A]" (solarman) {channel="solarman:logger:local:solar-pv1-current", unit="A"}
+Number:Power PV1_Power "PV1 Power [%d W]" (solarman) {channel="solarman:logger:local:solar-pv1-power", unit="W"}
+Number:ElectricPotential PV1_Voltage "PV1 Voltage [%d V]" (solarman) {channel="solarman:logger:local:solar-pv1-voltage", unit="V"}
+Number:ElectricCurrent PV2_Current "PV2 Current [%.1f A]" (solarman) {channel="solarman:logger:local:solar-pv2-current", unit="A"}
+Number:Power PV2_Power "PV2 Power [%d W]" (solarman) {channel="solarman:logger:local:solar-pv2-power", unit="W"}
+Number:ElectricPotential PV2_Voltage "PV2 Voltage [%d V]" (solarman) {channel="solarman:logger:local:solar-pv2-voltage", unit="V"}
+Number:Dimensionless Battery_SOC "Battery SOC [%d %%]" (solarman) {channel="solarman:logger:local:battery-battery-soc", unit="%"}
+Number:ElectricCurrent Battery_Current "Battery Current [%.1f A]" (solarman) {channel="solarman:logger:local:battery-battery-current", unit="A"}
+Number:Power Battery_Power "Battery Power [%d W]" (solarman) {channel="solarman:logger:local:battery-battery-power", unit="W"}
+Number:ElectricPotential Battery_Voltage "Battery Voltage [%.2f V]" (solarman) {channel="solarman:logger:local:battery-battery-voltage", unit="V"}
+Number:Temperature Battery_Temperature "Battery Temperature [%.1f °C]" (solarman) {channel="solarman:logger:local:battery-battery-temperature", unit="°C"}
+Number:Energy Daily_Battery_Charge "Daily Battery Charge [%.1f kWh]" (solarman) {channel="solarman:logger:local:battery-daily-battery-charge", unit="kWh"}
+Number:Energy Daily_Battery_Discharge "Daily Battery Discharge [%.1f kWh]" (solarman) {channel="solarman:logger:local:battery-daily-battery-discharge", unit="kWh"}
+Number:Energy Total_Battery_Charge "Total Battery Charge [%d kWh]" (solarman) {channel="solarman:logger:local:battery-total-battery-charge", unit="kWh"}
+Number:Energy Total_Battery_Discharge "Total Battery Discharge [%d kWh]" (solarman) {channel="solarman:logger:local:battery-total-battery-discharge", unit="kWh"}
+Number Alert "Alert [%s]" (solarman) {channel="solarman:logger:local:alert-alert"}
+```
+
+### `solarman.sitemap`
+
+Sitemap example for a SUN-12K-SG04LP3-EU inverter
+
+```perl
+sitemap solarman label="Solarman"
+{
+ Frame label="Inverter"{
+ Text item=Communication_Board_Version_No icon="solar"
+ Text item=Control_Board_Version_No icon="solar"
+ Text item=Inverter_Id icon="solar"
+ Text item=AC_Temperature icon="temperature"
+ Text item=DC_Temperature icon="temperature"
+ Text item=Inverter_L1_Power icon="poweroutlet"
+ Text item=Inverter_L2_Power icon="poweroutlet"
+ Text item=Inverter_L3_Power icon="poweroutlet"
+ Text item=Current_L1 icon="line"
+ Text item=Current_L2 icon="line"
+ Text item=Current_L3 icon="line"
+ }
+
+ Frame label="Battery"{
+ Text item=Battery_SOC icon="battery"
+ Text item=Battery_Current icon="current"
+ Text item=Battery_Power icon="power"
+ Text item=Battery_Voltage icon="voltage"
+ Text item=Battery_Temperature icon="temperature"
+ Text item=Daily_Battery_Charge icon="renewable"
+ Text item=Daily_Battery_Discharge icon="battery"
+ Text item=Total_Battery_Charge icon="renewable"
+ Text item=Total_Battery_Discharge icon="battery"
+ }
+
+ Frame label="Solar"{
+ Text item=Total_Production icon="solar"
+ Text item=Daily_Production icon="solar"
+ Text item=PV1_Current icon="solar"
+ Text item=PV1_Power icon="solar"
+ Text item=PV1_Voltage icon="solar"
+ Text item=PV2_Current icon="solar"
+ Text item=PV2_Power icon="solar"
+ Text item=PV2_Voltage icon="solar"
+ }
+
+ Frame label="Grid"{
+ Text item=Total_Grid_Production icon="power"
+ Text item=Total_Grid_Power icon="power"
+ Text item=External_CT_L1_Power icon="power"
+ Text item=External_CT_L2_Power icon="power"
+ Text item=External_CT_L3_Power icon="power"
+ Text item=Internal_CT_L1_Power icon="power"
+ Text item=Internal_CT_L2_Power icon="power"
+ Text item=Internal_CT_L3_Power icon="power"
+ Text item=Grid_Voltage_L1 icon="power"
+ Text item=Grid_Voltage_L2 icon="power"
+ Text item=Grid_Voltage_L3 icon="power"
+ Text item=Daily_Energy_Sold icon="power"
+ Text item=Total_Energy_Sold icon="power"
+ Text item=Daily_Energy_Bought icon="power"
+ Text item=Total_Energy_Bought icon="power"
+ }
+
+ Frame label="Load"{
+ Text item=Daily_Load_Consumption icon="power"
+ Text item=Total_Load_Consumption icon="power"
+ Text item=Load_L1_Power icon="power"
+ Text item=Load_L2_Power icon="power"
+ Text item=Load_L3_Power icon="power"
+ Text item=Load_Voltage_L1 icon="power"
+ Text item=Load_Voltage_L2 icon="power"
+ Text item=Load_Voltage_L3 icon="power"
+ Text item=Total_Load_Power icon="power"
+ }
+
+ Frame label="Alert"{
+ Text item=Alert icon="alert"
+ }
+}
+```
+
+## Acknowledgments
+
+The code's creation draws significant inspiration from [Stephan Joubert's Home Assistant plugin](https://github.com/StephanJoubert/home_assistant_solarman), which provides the inverter definitions used in the project.
+Additionally, the [pysolarmanv5 module](https://pysolarmanv5.readthedocs.io/en/latest/index.html) was a valuable resource, as it offers an excellent explanation of the Solarman V5 protocol.
diff --git a/bundles/org.openhab.binding.solarman/pom.xml b/bundles/org.openhab.binding.solarman/pom.xml
new file mode 100644
index 00000000000..a3446256ee2
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/pom.xml
@@ -0,0 +1,17 @@
+
+
+
+ 4.0.0
+
+
+ org.openhab.addons.bundles
+ org.openhab.addons.reactor.bundles
+ 4.3.0-SNAPSHOT
+
+
+ org.openhab.binding.solarman
+
+ openHAB Add-ons :: Bundles :: Solarman Binding
+
+
diff --git a/bundles/org.openhab.binding.solarman/src/main/feature/feature.xml b/bundles/org.openhab.binding.solarman/src/main/feature/feature.xml
new file mode 100644
index 00000000000..de6deb616f9
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/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.solarman/${project.version}
+
+
diff --git a/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/DefinitionParser.java b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/DefinitionParser.java
new file mode 100644
index 00000000000..93c1fcbb52e
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/DefinitionParser.java
@@ -0,0 +1,62 @@
+/**
+ * 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.solarman.internal;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Objects;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.solarman.internal.defmodel.InverterDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+
+/**
+ * The {@link DefinitionParser} is parses inverter definitions
+ *
+ * @author Catalin Sanda - Initial contribution
+ */
+@NonNullByDefault
+public class DefinitionParser {
+ private final Logger logger = LoggerFactory.getLogger(DefinitionParser.class);
+
+ private final ObjectMapper mapper;
+
+ public DefinitionParser() {
+ mapper = new ObjectMapper(new YAMLFactory());
+ }
+
+ @Nullable
+ public InverterDefinition parseDefinition(String definitionId) {
+ ClassLoader cl = Objects.requireNonNull(getClass().getClassLoader());
+ String definitionFileName = String.format("definitions/%s.yaml", definitionId);
+ try (InputStream is = cl.getResourceAsStream(definitionFileName)) {
+ if (is == null) {
+ logger.warn("Unable to read definition file {}", definitionFileName);
+ return null;
+ }
+
+ InverterDefinition inverterDefinition = mapper.readValue(is, InverterDefinition.class);
+ inverterDefinition.setInverterDefinitionId(definitionId);
+
+ return inverterDefinition;
+ } catch (IOException e) {
+ logger.warn("Error parsing definition with ID: {}", definitionId, e);
+ return null;
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/SolarmanBindingConstants.java b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/SolarmanBindingConstants.java
new file mode 100644
index 00000000000..1152bdec7fa
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/SolarmanBindingConstants.java
@@ -0,0 +1,30 @@
+/**
+ * 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.solarman.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.thing.ThingTypeUID;
+
+/**
+ * The {@link SolarmanBindingConstants} class defines common constants, which are
+ * used across the whole binding.
+ *
+ * @author Catalin Sanda - Initial contribution
+ */
+@NonNullByDefault
+public class SolarmanBindingConstants {
+
+ public static final String SOLARMAN_BINDING_ID = "solarman";
+ public static final ThingTypeUID THING_TYPE_SOLARMAN_LOGGER = new ThingTypeUID(SOLARMAN_BINDING_ID, "logger");
+ public static final String DYNAMIC_CHANNEL = "dynamic-channel";
+}
diff --git a/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/SolarmanHandlerFactory.java b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/SolarmanHandlerFactory.java
new file mode 100644
index 00000000000..543df075bc0
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/SolarmanHandlerFactory.java
@@ -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.solarman.internal;
+
+import java.util.Set;
+
+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 SolarmanHandlerFactory} is responsible for creating things and thing
+ * handlers.
+ *
+ * @author Catalin Sanda - Initial contribution
+ */
+@NonNullByDefault
+@Component(configurationPid = "binding.solarman", service = ThingHandlerFactory.class)
+public class SolarmanHandlerFactory extends BaseThingHandlerFactory {
+ private static final Set SUPPORTED_THING_TYPES_UIDS = Set
+ .of(SolarmanBindingConstants.THING_TYPE_SOLARMAN_LOGGER);
+
+ @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 (SolarmanBindingConstants.THING_TYPE_SOLARMAN_LOGGER.equals(thingTypeUID)) {
+ return new SolarmanLoggerHandler(thing);
+ }
+
+ return null;
+ }
+}
diff --git a/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/SolarmanLoggerConfiguration.java b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/SolarmanLoggerConfiguration.java
new file mode 100644
index 00000000000..4e8b082a14d
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/SolarmanLoggerConfiguration.java
@@ -0,0 +1,74 @@
+/**
+ * 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.solarman.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * The {@link SolarmanLoggerConfiguration} class contains fields mapping thing configuration parameters.
+ *
+ * @author Catalin Sanda - Initial contribution
+ */
+@NonNullByDefault
+public class SolarmanLoggerConfiguration {
+
+ /**
+ * Solarman Logger Thing Configuration Parameters
+ */
+ public String hostname = "";
+ public Integer port = 8899;
+ public String serialNumber = "";
+ public String inverterType = "sg04lp3";
+ public int refreshInterval = 30;
+ @Nullable
+ public String additionalRequests;
+
+ public SolarmanLoggerConfiguration() {
+ }
+
+ public SolarmanLoggerConfiguration(String hostname, Integer port, String serialNumber, String inverterType,
+ int refreshInterval, @Nullable String additionalRequests) {
+ this.hostname = hostname;
+ this.port = port;
+ this.serialNumber = serialNumber;
+ this.inverterType = inverterType;
+ this.refreshInterval = refreshInterval;
+ this.additionalRequests = additionalRequests;
+ }
+
+ public String getHostname() {
+ return hostname;
+ }
+
+ public Integer getPort() {
+ return port;
+ }
+
+ public String getSerialNumber() {
+ return serialNumber;
+ }
+
+ public String getInverterType() {
+ return inverterType;
+ }
+
+ public int getRefreshInterval() {
+ return refreshInterval;
+ }
+
+ @Nullable
+ public String getAdditionalRequests() {
+ return additionalRequests;
+ }
+}
diff --git a/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/SolarmanLoggerHandler.java b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/SolarmanLoggerHandler.java
new file mode 100644
index 00000000000..a5dfb68c682
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/SolarmanLoggerHandler.java
@@ -0,0 +1,225 @@
+/**
+ * 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.solarman.internal;
+
+import static org.openhab.binding.solarman.internal.SolarmanBindingConstants.DYNAMIC_CHANNEL;
+
+import java.util.AbstractMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.solarman.internal.channel.BaseChannelConfig;
+import org.openhab.binding.solarman.internal.channel.SolarmanChannelManager;
+import org.openhab.binding.solarman.internal.defmodel.InverterDefinition;
+import org.openhab.binding.solarman.internal.defmodel.ParameterItem;
+import org.openhab.binding.solarman.internal.defmodel.Request;
+import org.openhab.binding.solarman.internal.defmodel.Validation;
+import org.openhab.binding.solarman.internal.modbus.SolarmanLoggerConnector;
+import org.openhab.binding.solarman.internal.modbus.SolarmanV5Protocol;
+import org.openhab.binding.solarman.internal.updater.SolarmanChannelUpdater;
+import org.openhab.binding.solarman.internal.updater.SolarmanProcessResult;
+import org.openhab.core.thing.Channel;
+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.thing.binding.builder.ThingBuilder;
+import org.openhab.core.types.Command;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link SolarmanLoggerHandler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author Catalin Sanda - Initial contribution
+ */
+@NonNullByDefault
+public class SolarmanLoggerHandler extends BaseThingHandler {
+ private final Logger logger = LoggerFactory.getLogger(SolarmanLoggerHandler.class);
+
+ private final DefinitionParser definitionParser;
+ private final SolarmanChannelManager solarmanChannelManager;
+ @Nullable
+ private volatile ScheduledFuture> scheduledFuture;
+
+ public SolarmanLoggerHandler(Thing thing) {
+ super(thing);
+ this.definitionParser = new DefinitionParser();
+ this.solarmanChannelManager = new SolarmanChannelManager();
+ }
+
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+ }
+
+ @Override
+ public void initialize() {
+ updateStatus(ThingStatus.UNKNOWN);
+
+ SolarmanLoggerConfiguration config = getConfigAs(SolarmanLoggerConfiguration.class);
+ SolarmanLoggerConnector solarmanLoggerConnector = new SolarmanLoggerConnector(config);
+
+ List staticChannels = thing.getChannels().stream()
+ .filter(channel -> !channel.getProperties().containsKey(DYNAMIC_CHANNEL)).toList();
+
+ InverterDefinition inverterDefinition = definitionParser.parseDefinition(config.inverterType);
+
+ if (inverterDefinition == null) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+ "Unable to find a definition for the provided inverter type");
+ return;
+ } else {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Found definition for {}", config.inverterType);
+ }
+ }
+ SolarmanV5Protocol solarmanV5Protocol = new SolarmanV5Protocol(config);
+
+ String additionalRequests = Objects.requireNonNullElse(config.getAdditionalRequests(), "");
+
+ List mergedRequests = !additionalRequests.isBlank()
+ ? mergeRequests(inverterDefinition.getRequests(), extractAdditionalRequests(additionalRequests))
+ : inverterDefinition.getRequests();
+
+ Map paramToChannelMapping = mergeMaps(
+ extractChannelMappingFromChannels(staticChannels),
+ setupChannelsForInverterDefinition(inverterDefinition));
+
+ SolarmanChannelUpdater solarmanChannelUpdater = new SolarmanChannelUpdater(this::updateState);
+
+ scheduledFuture = scheduler
+ .scheduleWithFixedDelay(
+ () -> queryLoggerAndUpdateState(solarmanLoggerConnector, solarmanV5Protocol, mergedRequests,
+ paramToChannelMapping, solarmanChannelUpdater),
+ 0, config.refreshInterval, TimeUnit.SECONDS);
+ }
+
+ private void queryLoggerAndUpdateState(SolarmanLoggerConnector solarmanLoggerConnector,
+ SolarmanV5Protocol solarmanV5Protocol, List mergedRequests,
+ Map paramToChannelMapping, SolarmanChannelUpdater solarmanChannelUpdater) {
+ try {
+ SolarmanProcessResult solarmanProcessResult = solarmanChannelUpdater.fetchDataFromLogger(mergedRequests,
+ solarmanLoggerConnector, solarmanV5Protocol, paramToChannelMapping);
+
+ if (solarmanProcessResult.hasSuccessfulResponses()) {
+ updateStatus(ThingStatus.ONLINE);
+ } else {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+ solarmanProcessResult.toString());
+ }
+ } catch (Exception e) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
+ }
+ }
+
+ private Map mergeMaps(Map map1, Map map2) {
+ return Stream.concat(map1.entrySet().stream(), map2.entrySet().stream())
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v1));
+ }
+
+ private Map extractChannelMappingFromChannels(List channels) {
+ return channels.stream().map(channel -> {
+ BaseChannelConfig bcc = channel.getConfiguration().as(BaseChannelConfig.class);
+
+ @Nullable
+ String label = channel.getLabel();
+ if (label == null) {
+ throw new IllegalStateException("Channel label should not be null");
+ }
+
+ return new AbstractMap.SimpleEntry<>(new ParameterItem(label, "N/A", "N/A", bcc.uom, bcc.scale, bcc.rule,
+ parseRegisters(bcc.registers), "N/A", new Validation(), bcc.offset, Boolean.FALSE),
+ channel.getUID());
+ }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+ }
+
+ private List parseRegisters(String registers) {
+ String[] tokens = registers.split(",");
+ Pattern pattern = Pattern.compile("\\s*(0x[\\da-fA-F]+|[\\d]+)\\s*");
+ return Stream.of(tokens).map(pattern::matcher).filter(Matcher::matches).map(matcher -> matcher.group(1))
+ .map(SolarmanLoggerHandler::parseNumber).toList();
+ }
+
+ // For now just concatenate the list, in the future, merge overlapping requests
+ private List mergeRequests(List requestList1, List requestList2) {
+ return Stream.concat(requestList1.stream(), requestList2.stream()).collect(Collectors.toList());
+ }
+
+ private List extractAdditionalRequests(String channels) {
+ String[] tokens = channels.split(",");
+ Pattern pattern = Pattern.compile(
+ "\\s*(0x[\\da-fA-F]+|[\\d]+)\\s*:\\s*(0x[\\da-fA-F]+|[\\d]+)\\s*-\\s*(0x[\\da-fA-F]+|[\\d]+)\\s*");
+
+ return Stream.of(tokens).map(pattern::matcher).filter(Matcher::matches).map(matcher -> {
+ try {
+ int functionCode = parseNumber(matcher.group(1));
+ int start = parseNumber(matcher.group(2));
+ int end = parseNumber(matcher.group(3));
+ return new Request(functionCode, start, end);
+ } catch (NumberFormatException e) {
+ logger.debug("Invalid number format in token: {} , ignoring additional requests", matcher.group(), e);
+ return new Request(-1, 0, 0);
+ }
+ }).filter(request -> request.getMbFunctioncode() > 0).collect(Collectors.toList());
+ }
+
+ private static int parseNumber(String number) {
+ return number.startsWith("0x") ? Integer.parseInt(number.substring(2), 16) : Integer.parseInt(number);
+ }
+
+ private Map setupChannelsForInverterDefinition(InverterDefinition inverterDefinition) {
+ ThingBuilder thingBuilder = editThing();
+
+ List oldDynamicChannels = thing.getChannels().stream()
+ .filter(channel -> channel.getProperties().containsKey(DYNAMIC_CHANNEL)).toList();
+
+ Map newDynamicItemChannelMap = solarmanChannelManager.generateItemChannelMap(thing,
+ inverterDefinition);
+
+ // Remove old dynamic channels
+ thingBuilder.withoutChannels(oldDynamicChannels);
+
+ // Add new dynamic channels
+ newDynamicItemChannelMap.values().forEach(thingBuilder::withChannel);
+
+ updateThing(thingBuilder.build());
+
+ logger.debug("Updated thing with id {} and {} channels", thing.getThingTypeUID(), thing.getChannels().size());
+
+ return newDynamicItemChannelMap.entrySet().stream()
+ .map(entry -> new AbstractMap.SimpleEntry<>(entry.getKey(), entry.getValue().getUID()))
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+
+ ScheduledFuture> scheduledFuture = this.scheduledFuture;
+ if (scheduledFuture != null) {
+ scheduledFuture.cancel(false);
+ this.scheduledFuture = null;
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/channel/BaseChannelConfig.java b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/channel/BaseChannelConfig.java
new file mode 100644
index 00000000000..1eb99cfac9e
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/channel/BaseChannelConfig.java
@@ -0,0 +1,30 @@
+/**
+ * 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.solarman.internal.channel;
+
+import java.math.BigDecimal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * @author Catalin Sanda - Initial contribution
+ */
+@NonNullByDefault
+public class BaseChannelConfig {
+ public @Nullable String uom;
+ public BigDecimal scale = BigDecimal.ONE;
+ public Integer rule = 1;
+ public BigDecimal offset = BigDecimal.ZERO;
+ public String registers = "";
+}
diff --git a/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/channel/SolarmanChannelManager.java b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/channel/SolarmanChannelManager.java
new file mode 100644
index 00000000000..5fad5438aa8
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/channel/SolarmanChannelManager.java
@@ -0,0 +1,100 @@
+/**
+ * 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.solarman.internal.channel;
+
+import static org.openhab.binding.solarman.internal.SolarmanBindingConstants.DYNAMIC_CHANNEL;
+import static org.openhab.binding.solarman.internal.typeprovider.ChannelUtils.escapeName;
+
+import java.math.BigDecimal;
+import java.util.AbstractMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.solarman.internal.defmodel.InverterDefinition;
+import org.openhab.binding.solarman.internal.defmodel.ParameterItem;
+import org.openhab.binding.solarman.internal.typeprovider.ChannelUtils;
+import org.openhab.core.config.core.Configuration;
+import org.openhab.core.thing.Channel;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.binding.builder.ChannelBuilder;
+import org.openhab.core.thing.type.ChannelKind;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * @author Catalin Sanda - Initial contribution
+ */
+@NonNullByDefault
+public class SolarmanChannelManager {
+ private final ObjectMapper objectMapper;
+
+ public SolarmanChannelManager() {
+ objectMapper = new ObjectMapper();
+ }
+
+ public Map generateItemChannelMap(Thing thing, InverterDefinition inverterDefinition) {
+ return inverterDefinition.getParameters().stream().flatMap(parameter -> {
+ String groupName = escapeName(parameter.getGroup());
+
+ return parameter.getItems().stream().map(item -> {
+ String channelId = groupName + "-" + escapeName(item.getName());
+
+ Channel channel = ChannelBuilder.create(new ChannelUID(thing.getUID(), channelId))
+ .withType(ChannelUtils.computeChannelTypeId(inverterDefinition.getInverterDefinitionId(),
+ groupName, item.getName()))
+ .withLabel(item.getName()).withKind(ChannelKind.STATE)
+ .withAcceptedItemType(ChannelUtils.getItemType(item))
+ .withProperties(Map.of(DYNAMIC_CHANNEL, Boolean.TRUE.toString()))
+ .withConfiguration(buildConfigurationFromItem(item)).build();
+ return new AbstractMap.SimpleEntry<>(item, channel);
+ });
+ }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+ }
+
+ private Configuration buildConfigurationFromItem(ParameterItem item) {
+ Configuration configuration = new Configuration();
+
+ BaseChannelConfig baseChannelConfig = new BaseChannelConfig();
+
+ BigDecimal offset = item.getOffset();
+ if (offset != null) {
+ baseChannelConfig.offset = offset;
+ }
+
+ BigDecimal scale = item.getScale();
+ if (scale != null) {
+ baseChannelConfig.scale = scale;
+ }
+
+ baseChannelConfig.rule = item.getRule();
+ baseChannelConfig.registers = convertRegisters(item.getRegisters());
+ baseChannelConfig.uom = item.getUom();
+
+ Map configurationMap = objectMapper.convertValue(baseChannelConfig, new TypeReference<>() {
+ });
+
+ configurationMap.forEach(configuration::put);
+
+ return configuration;
+ }
+
+ private String convertRegisters(List registers) {
+ return "["
+ + registers.stream().map(register -> String.format("0x%04X", register)).collect(Collectors.joining(","))
+ + "]";
+ }
+}
diff --git a/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/defmodel/InverterDefinition.java b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/defmodel/InverterDefinition.java
new file mode 100644
index 00000000000..b4c4f958107
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/defmodel/InverterDefinition.java
@@ -0,0 +1,55 @@
+/**
+ * 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.solarman.internal.defmodel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+/**
+ * @author Catalin Sanda - Initial contribution
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+@NonNullByDefault
+public class InverterDefinition {
+ private String inverterDefinitionId = "";
+ private List requests = new ArrayList<>();
+ private List parameters = new ArrayList<>();
+
+ public String getInverterDefinitionId() {
+ return inverterDefinitionId;
+ }
+
+ public void setInverterDefinitionId(String inverterDefinitionId) {
+ this.inverterDefinitionId = inverterDefinitionId;
+ }
+
+ public List getRequests() {
+ return requests;
+ }
+
+ public void setRequests(List requests) {
+ this.requests = requests;
+ }
+
+ public List getParameters() {
+ return parameters;
+ }
+
+ public void setParameters(List parameters) {
+ this.parameters = parameters;
+ }
+}
diff --git a/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/defmodel/Parameter.java b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/defmodel/Parameter.java
new file mode 100644
index 00000000000..0e43f9eed4e
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/defmodel/Parameter.java
@@ -0,0 +1,46 @@
+/**
+ * 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.solarman.internal.defmodel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+/**
+ * @author Catalin Sanda - Initial contribution
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+@NonNullByDefault
+public class Parameter {
+ private String group = "";
+ private List items = new ArrayList();
+
+ public String getGroup() {
+ return group;
+ }
+
+ public void setGroup(String group) {
+ this.group = group;
+ }
+
+ public List getItems() {
+ return items;
+ }
+
+ public void setItems(List items) {
+ this.items = items;
+ }
+}
diff --git a/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/defmodel/ParameterItem.java b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/defmodel/ParameterItem.java
new file mode 100644
index 00000000000..bc5a9cbf7cd
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/defmodel/ParameterItem.java
@@ -0,0 +1,156 @@
+/**
+ * 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.solarman.internal.defmodel;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+/**
+ * @author Catalin Sanda - Initial contribution
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+@NonNullByDefault
+public class ParameterItem {
+ private String name = "";
+ @Nullable
+ private String itemClass;
+ @Nullable
+ private String stateClass;
+ @Nullable
+ private String uom;
+ @Nullable
+ private BigDecimal scale;
+ private Integer rule = 1;
+ private List registers = new ArrayList<>();
+ @Nullable
+ private String icon;
+ @Nullable
+ private Validation validation;
+ @Nullable
+ private BigDecimal offset;
+ @Nullable
+ private Boolean isstr;
+
+ public ParameterItem() {
+ }
+
+ public ParameterItem(String name, @Nullable String itemClass, @Nullable String stateClass, @Nullable String uom,
+ @Nullable BigDecimal scale, Integer rule, List registers, @Nullable String icon,
+ @Nullable Validation validation, @Nullable BigDecimal offset, @Nullable Boolean isstr) {
+ this.name = name;
+ this.itemClass = itemClass;
+ this.stateClass = stateClass;
+ this.uom = uom;
+ this.scale = scale;
+ this.rule = rule;
+ this.registers = registers;
+ this.icon = icon;
+ this.validation = validation;
+ this.offset = offset;
+ this.isstr = isstr;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public @Nullable String getStateClass() {
+ return stateClass;
+ }
+
+ public void setStateClass(String stateClass) {
+ this.stateClass = stateClass;
+ }
+
+ public @Nullable String getUom() {
+ return uom;
+ }
+
+ public void setUom(String uom) {
+ this.uom = uom;
+ }
+
+ public @Nullable BigDecimal getScale() {
+ return scale;
+ }
+
+ public void setScale(BigDecimal scale) {
+ this.scale = scale;
+ }
+
+ public Integer getRule() {
+ return rule;
+ }
+
+ public void setRule(Integer rule) {
+ this.rule = rule;
+ }
+
+ public List getRegisters() {
+ return registers;
+ }
+
+ public void setRegisters(List registers) {
+ this.registers = registers;
+ }
+
+ public @Nullable String getIcon() {
+ return icon;
+ }
+
+ public void setIcon(String icon) {
+ this.icon = icon;
+ }
+
+ public @Nullable Validation getValidation() {
+ return validation;
+ }
+
+ public void setValidation(Validation validation) {
+ this.validation = validation;
+ }
+
+ public @Nullable BigDecimal getOffset() {
+ return offset;
+ }
+
+ public void setOffset(BigDecimal offset) {
+ this.offset = offset;
+ }
+
+ public @Nullable Boolean getIsstr() {
+ return isstr;
+ }
+
+ public void setIsstr(Boolean isstr) {
+ this.isstr = isstr;
+ }
+
+ public @Nullable String getItemClass() {
+ return itemClass;
+ }
+
+ public void setItemClass(String itemClass) {
+ this.itemClass = itemClass;
+ }
+}
diff --git a/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/defmodel/Request.java b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/defmodel/Request.java
new file mode 100644
index 00000000000..b6659cd8cf6
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/defmodel/Request.java
@@ -0,0 +1,74 @@
+/**
+ * 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.solarman.internal.defmodel;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * @author Catalin Sanda - Initial contribution
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+@NonNullByDefault
+public class Request {
+ public static final Request NONE = new Request(-1, 0, 0);
+ private Integer start = 0;
+ private Integer end = 0;
+ @JsonProperty("mb_functioncode")
+ private Integer mbFunctioncode = 0x03;
+
+ public Request() {
+ }
+
+ public Request(Integer mbFunctioncode, Integer start, Integer end) {
+ this.mbFunctioncode = mbFunctioncode;
+ this.start = start;
+ this.end = end;
+ }
+
+ public Integer getStart() {
+ return start;
+ }
+
+ public void setStart(Integer start) {
+ this.start = start;
+ }
+
+ public Integer getEnd() {
+ return end;
+ }
+
+ public void setEnd(Integer end) {
+ this.end = end;
+ }
+
+ public Integer getMbFunctioncode() {
+ return mbFunctioncode;
+ }
+
+ public void setMbFunctioncode(Integer mbFunctioncode) {
+ this.mbFunctioncode = mbFunctioncode;
+ }
+
+ @Override
+ public String toString() {
+ if (this == NONE) {
+ return "N/A";
+ } else {
+ return "Request{" + "start=0x" + Integer.toHexString(start) + ", end=0x" + Integer.toHexString(end)
+ + ", mbFunctioncode=0x" + Integer.toHexString(mbFunctioncode) + '}';
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/defmodel/Validation.java b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/defmodel/Validation.java
new file mode 100644
index 00000000000..3ca171e78ae
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/defmodel/Validation.java
@@ -0,0 +1,54 @@
+/**
+ * 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.solarman.internal.defmodel;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * @author Catalin Sanda - Initial contribution
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+@NonNullByDefault
+public class Validation {
+ private Integer max = 0;
+ @JsonProperty("invalidate_all")
+ private Object invalidateAll = new Object();
+ private Integer min = 0;
+
+ public Integer getMax() {
+ return max;
+ }
+
+ public void setMax(Integer max) {
+ this.max = max;
+ }
+
+ public Object getInvalidateAll() {
+ return invalidateAll;
+ }
+
+ public void setInvalidateAll(Object invalidateAll) {
+ this.invalidateAll = invalidateAll;
+ }
+
+ public Integer getMin() {
+ return min;
+ }
+
+ public void setMin(Integer min) {
+ this.min = min;
+ }
+}
diff --git a/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/modbus/CRC16Modbus.java b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/modbus/CRC16Modbus.java
new file mode 100644
index 00000000000..7bb9658e089
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/modbus/CRC16Modbus.java
@@ -0,0 +1,45 @@
+/**
+ * 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.solarman.internal.modbus;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * @author Catalin Sanda - Initial contribution
+ */
+@NonNullByDefault
+public class CRC16Modbus {
+ private static final int[] CRC_TABLE = new int[256];
+
+ static {
+ for (int i = 0; i < 256; i++) {
+ int crc = i;
+ for (int j = 0; j < 8; j++) {
+ if ((crc & 0x0001) != 0) {
+ crc = (crc >>> 1) ^ 0xA001;
+ } else {
+ crc = crc >>> 1;
+ }
+ }
+ CRC_TABLE[i] = crc;
+ }
+ }
+
+ public static int calculate(byte[] data) {
+ int crc = 0xFFFF;
+ for (byte b : data) {
+ crc = (crc >>> 8) ^ CRC_TABLE[(crc ^ b) & 0xFF];
+ }
+ return crc;
+ }
+}
diff --git a/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/modbus/SolarmanLoggerConnection.java b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/modbus/SolarmanLoggerConnection.java
new file mode 100644
index 00000000000..cf316734929
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/modbus/SolarmanLoggerConnection.java
@@ -0,0 +1,131 @@
+/**
+ * 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.solarman.internal.modbus;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketTimeoutException;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.solarman.internal.modbus.exception.SolarmanConnectionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Catalin Sanda - Initial contribution
+ */
+@NonNullByDefault
+public class SolarmanLoggerConnection implements AutoCloseable {
+ private final Logger logger = LoggerFactory.getLogger(SolarmanLoggerConnection.class);
+ @Nullable
+ private Socket socket;
+
+ public SolarmanLoggerConnection(String hostName, int port) {
+ SocketAddress sockaddr = new InetSocketAddress(hostName, port);
+ Socket localSocket = connectSocket(sockaddr);
+
+ if (localSocket == null) {
+ logger.debug("Error creating socket");
+ } else {
+ socket = localSocket;
+ }
+ }
+
+ public byte[] sendRequest(byte[] reqFrame) throws SolarmanConnectionException {
+ // Will not be used by multiple threads, so not bothering making it thread safe for now
+ Socket localSocket = socket;
+
+ if (localSocket == null) {
+ throw new SolarmanConnectionException("Socket is null, not reading data this time");
+ }
+
+ try {
+ logger.trace("Request frame: {}", bytesToHex(reqFrame));
+ localSocket.getOutputStream().write(reqFrame);
+ } catch (IOException e) {
+ logger.debug("Unable to send frame to logger");
+ return new byte[0];
+ }
+
+ byte[] buffer = new byte[1024];
+ int attempts = 5;
+
+ while (attempts > 0) {
+ attempts--;
+ try {
+ int bytesRead = localSocket.getInputStream().read(buffer);
+ if (bytesRead < 0) {
+ throw new SolarmanConnectionException("No data received");
+ } else {
+ byte[] data = Arrays.copyOfRange(buffer, 0, bytesRead);
+ if (logger.isDebugEnabled()) {
+ logger.trace("Response frame: {}", bytesToHex(data));
+ }
+ return data;
+ }
+ } catch (SocketTimeoutException e) {
+ logger.debug("Connection timeout", e);
+ if (attempts == 0) {
+ throw new SolarmanConnectionException("Too many socket timeouts", e);
+ }
+ } catch (IOException e) {
+ throw new SolarmanConnectionException("Error reading data from ", e);
+ }
+ }
+
+ return new byte[0];
+ }
+
+ private static String bytesToHex(byte[] bytes) {
+ return IntStream.range(0, bytes.length).mapToObj(i -> String.format("%02X", bytes[i]))
+ .collect(Collectors.joining());
+ }
+
+ private @Nullable Socket connectSocket(SocketAddress socketAddress) {
+ try {
+ Socket clientSocket = new Socket();
+
+ clientSocket.setSoTimeout(10_000);
+ clientSocket.connect(socketAddress, 10_000);
+
+ return clientSocket;
+ } catch (IOException e) {
+ logger.debug("Could not open socket on IP {}", socketAddress, e);
+ return null;
+ }
+ }
+
+ @Override
+ public void close() {
+ Socket localSocket = socket;
+ if (localSocket != null && !localSocket.isClosed()) {
+ try {
+ localSocket.close();
+ } catch (IOException e) {
+ logger.debug("Unable to close connection");
+ }
+ }
+ socket = null;
+ }
+
+ public boolean isConnected() {
+ Socket localSocket = socket;
+ return localSocket != null && localSocket.isConnected();
+ }
+}
diff --git a/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/modbus/SolarmanLoggerConnector.java b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/modbus/SolarmanLoggerConnector.java
new file mode 100644
index 00000000000..b3ef66bdd8d
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/modbus/SolarmanLoggerConnector.java
@@ -0,0 +1,33 @@
+/**
+ * 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.solarman.internal.modbus;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.solarman.internal.SolarmanLoggerConfiguration;
+
+/**
+ * @author Catalin Sanda - Initial contribution
+ */
+@NonNullByDefault
+public class SolarmanLoggerConnector {
+ private final SolarmanLoggerConfiguration solarmanLoggerConfiguration;
+
+ public SolarmanLoggerConnector(SolarmanLoggerConfiguration solarmanLoggerConfiguration) {
+ this.solarmanLoggerConfiguration = solarmanLoggerConfiguration;
+ }
+
+ public SolarmanLoggerConnection createConnection() {
+ return new SolarmanLoggerConnection(solarmanLoggerConfiguration.getHostname(),
+ solarmanLoggerConfiguration.getPort());
+ }
+}
diff --git a/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/modbus/SolarmanV5Protocol.java b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/modbus/SolarmanV5Protocol.java
new file mode 100644
index 00000000000..16f484b6d78
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/modbus/SolarmanV5Protocol.java
@@ -0,0 +1,252 @@
+/**
+ * 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.solarman.internal.modbus;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.solarman.internal.SolarmanLoggerConfiguration;
+import org.openhab.binding.solarman.internal.modbus.exception.SolarmanAuthenticationException;
+import org.openhab.binding.solarman.internal.modbus.exception.SolarmanConnectionException;
+import org.openhab.binding.solarman.internal.modbus.exception.SolarmanException;
+import org.openhab.binding.solarman.internal.modbus.exception.SolarmanProtocolException;
+
+/**
+ * @author Catalin Sanda - Initial contribution
+ */
+@NonNullByDefault
+public class SolarmanV5Protocol {
+ private final SolarmanLoggerConfiguration solarmanLoggerConfiguration;
+
+ public SolarmanV5Protocol(SolarmanLoggerConfiguration solarmanLoggerConfiguration) {
+ this.solarmanLoggerConfiguration = solarmanLoggerConfiguration;
+ }
+
+ public Map readRegisters(SolarmanLoggerConnection solarmanLoggerConnection, byte mbFunctionCode,
+ int firstReg, int lastReg) throws SolarmanException {
+ byte[] solarmanV5Frame = buildSolarmanV5Frame(mbFunctionCode, firstReg, lastReg);
+ byte[] respFrame = solarmanLoggerConnection.sendRequest(solarmanV5Frame);
+ if (respFrame.length > 0) {
+ byte[] modbusRespFrame = extractModbusResponseFrame(respFrame, solarmanV5Frame);
+ return parseModbusReadHoldingRegistersResponse(modbusRespFrame, firstReg, lastReg);
+ } else {
+ throw new SolarmanConnectionException("Response frame was empty");
+ }
+ }
+
+ /**
+ * Builds a SolarMAN V5 frame to request data from firstReg to lastReg.
+ * Frame format is based on
+ * Solarman V5 Protocol
+ *
+ * @param mbFunctionCode
+ * @param firstReg - the start register
+ * @param lastReg - the end register
+ * @return byte array containing the Solarman V5 frame
+ */
+ protected byte[] buildSolarmanV5Frame(byte mbFunctionCode, int firstReg, int lastReg) {
+ byte[] requestPayload = buildSolarmanV5FrameRequestPayload(mbFunctionCode, firstReg, lastReg);
+ byte[] header = buildSolarmanV5FrameHeader(requestPayload.length);
+ byte[] trailer = buildSolarmanV5FrameTrailer(header, requestPayload);
+
+ return ByteBuffer.allocate(header.length + requestPayload.length + trailer.length).put(header)
+ .put(requestPayload).put(trailer).array();
+ }
+
+ private byte[] buildSolarmanV5FrameTrailer(byte[] header, byte[] requestPayload) {
+ byte[] headerAndPayload = ByteBuffer.allocate(header.length + requestPayload.length).put(header)
+ .put(requestPayload).array();
+ // (one byte) – Denotes the V5 frame checksum. The checksum is computed on the entire V5 frame except for Start,
+ // Checksum (obviously!) and End.
+ // Note, that this field is completely separate to the Modbus RTU checksum, which coincidentally, is the two
+ // bytes immediately preceding this field.
+ byte[] checksum = new byte[] {
+ computeChecksum(Arrays.copyOfRange(headerAndPayload, 1, headerAndPayload.length)) };
+
+ // (one byte) – Denotes the end of the V5 frame. Always 0x15.
+ byte[] end = new byte[] { (byte) 0x15 };
+
+ return ByteBuffer.allocate(checksum.length + end.length).put(checksum).put(end).array();
+ }
+
+ private byte computeChecksum(byte[] frame) {
+ // [-91, 23, 0, 16, 69, 0, 0, 46, -13, 90, 102, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 0, 0, 0, 39,
+ // 5, -48, 122, 21]
+ int checksumValue = 0;
+ for (byte b : frame) {
+ checksumValue += Byte.toUnsignedInt(b);
+ }
+ return (byte) (checksumValue & 0xFF);
+ }
+
+ private byte[] buildSolarmanV5FrameHeader(int payloadSize) {
+ // (one byte) Denotes the start of the V5 frame. Always 0xA5.
+ byte[] start = new byte[] { (byte) 0xA5 };
+
+ // (two bytes) Payload length
+ byte[] length = ByteBuffer.allocate(Short.BYTES).order(ByteOrder.LITTLE_ENDIAN).putShort((short) payloadSize)
+ .array();
+
+ // (two bytes) – Describes the type of V5 frame. For Modbus RTU requests, the control code is 0x4510. For Modbus
+ // RTU responses, the control code is 0x1510.
+ byte[] controlCode = new byte[] { (byte) 0x10, (byte) 0x45 };
+
+ // (two bytes) – This field acts as a two-way sequence number. On outgoing requests, the first byte of this
+ // field is echoed back in the same position on incoming responses.
+ // This is done by initialising this byte to a random value, and incrementing for each subsequent request.
+ // The second byte is incremented by the data logging stick for every response sent (either to Solarman Cloud or
+ // local requests).
+ // Note: the increment part is not implemented yet
+ byte[] serial = new byte[] { (byte) 0x00, (byte) 0x00 };
+
+ // (four bytes) – Serial number of Solarman data logging stick
+ byte[] loggerSerial = ByteBuffer.allocate(Integer.BYTES).order(ByteOrder.LITTLE_ENDIAN)
+ .putInt((int) Long.parseUnsignedLong(solarmanLoggerConfiguration.getSerialNumber())).array();
+
+ // Append all fields into the header
+ return ByteBuffer
+ .allocate(start.length + length.length + controlCode.length + serial.length + loggerSerial.length)
+ .put(start).put(length).put(controlCode).put(serial).put(loggerSerial).array();
+ }
+
+ protected byte[] buildSolarmanV5FrameRequestPayload(byte mbFunctionCode, int firstReg, int lastReg) {
+ // (one byte) – Denotes the frame type.
+ byte[] frameType = new byte[] { 0x02 };
+ // (two bytes) – Denotes the sensor type.
+ byte[] sensorType = new byte[] { 0x00, 0x00 };
+ // (four bytes) – Denotes the frame total working time. See corresponding response field of same name for
+ // further details.
+ byte[] totalWorkingTime = new byte[] { 0x00, 0x00, 0x00, 0x00 };
+ // (four bytes) – Denotes the frame power on time.
+ byte[] powerOnTime = new byte[] { 0x00, 0x00, 0x00, 0x00 };
+ // Denotes the frame offset time.
+ byte[] offsetTime = new byte[] { 0x00, 0x00, 0x00, 0x00 };
+ // (variable length) – Modbus RTU request frame.
+ byte[] requestFrame = buildModbusReadHoldingRegistersRequestFrame((byte) 0x01, mbFunctionCode, firstReg,
+ lastReg);
+
+ return ByteBuffer
+ .allocate(frameType.length + sensorType.length + totalWorkingTime.length + powerOnTime.length
+ + offsetTime.length + requestFrame.length)
+ .put(frameType).put(sensorType).put(totalWorkingTime).put(powerOnTime).put(offsetTime).put(requestFrame)
+ .array();
+ }
+
+ /**
+ * Based on Function 03 (03hex) Read Holding
+ * Registers
+ *
+ * @param slaveId - Slave Address
+ * @param mbFunctionCode -
+ * @param firstReg - Starting Address
+ * @param lastReg - Ending Address
+ * @return byte array containing the Modbus request frame
+ */
+ protected byte[] buildModbusReadHoldingRegistersRequestFrame(byte slaveId, byte mbFunctionCode, int firstReg,
+ int lastReg) {
+ int regCount = lastReg - firstReg + 1;
+ byte[] req = ByteBuffer.allocate(6).put(slaveId).put(mbFunctionCode).putShort((short) firstReg)
+ .putShort((short) regCount).array();
+ byte[] crc = ByteBuffer.allocate(Short.BYTES).order(ByteOrder.LITTLE_ENDIAN)
+ .putShort((short) CRC16Modbus.calculate(req)).array();
+
+ return ByteBuffer.allocate(req.length + crc.length).put(req).put(crc).array();
+ }
+
+ protected Map parseModbusReadHoldingRegistersResponse(byte @Nullable [] frame, int firstReg,
+ int lastReg) throws SolarmanProtocolException {
+ int regCount = lastReg - firstReg + 1;
+ Map registers = new HashMap<>();
+ int expectedFrameDataLen = 2 + 1 + regCount * 2;
+ if (frame == null || frame.length < expectedFrameDataLen + 2) {
+ throw new SolarmanProtocolException("Modbus frame is too short or empty");
+ }
+
+ int actualCrc = ByteBuffer.wrap(frame, expectedFrameDataLen, 2).order(ByteOrder.LITTLE_ENDIAN).getShort()
+ & 0xFFFF;
+ int expectedCrc = CRC16Modbus.calculate(Arrays.copyOfRange(frame, 0, expectedFrameDataLen));
+
+ if (actualCrc != expectedCrc) {
+ throw new SolarmanProtocolException(
+ String.format("Modbus frame crc is not valid. Expected %04x, got %04x", expectedCrc, actualCrc));
+ }
+
+ for (int i = 0; i < regCount; i++) {
+ int p1 = 3 + (i * 2);
+ ByteBuffer order = ByteBuffer.wrap(frame, p1, 2).order(ByteOrder.BIG_ENDIAN);
+ byte[] array = new byte[] { order.get(), order.get() };
+ registers.put(i + firstReg, array);
+ }
+
+ return registers;
+ }
+
+ protected byte[] extractModbusResponseFrame(byte @Nullable [] responseFrame, byte[] requestFrame)
+ throws SolarmanException {
+ if (responseFrame == null || responseFrame.length == 0) {
+ throw new SolarmanProtocolException("No response frame");
+ } else if (responseFrame.length == 29) {
+ parseResponseErrorCode(responseFrame, requestFrame);
+ throw new IllegalStateException("This should never be reached as previous method should always throw");
+ } else if (responseFrame.length < (29 + 4)) {
+ throw new SolarmanProtocolException("Response frame is too short");
+ } else if (responseFrame[0] != (byte) 0xA5) {
+ throw new SolarmanProtocolException("Response frame has invalid starting byte");
+ } else if (responseFrame[responseFrame.length - 1] != (byte) 0x15) {
+ throw new SolarmanProtocolException("Response frame has invalid ending byte");
+ }
+
+ return Arrays.copyOfRange(responseFrame, 25, responseFrame.length - 2);
+ }
+
+ protected void parseResponseErrorCode(byte[] responseFrame, byte[] requestFrame) throws SolarmanException {
+ if (responseFrame[0] == (byte) 0xA5 && responseFrame[1] == (byte) 0x10
+ && !Arrays.equals(Arrays.copyOfRange(responseFrame, 7, 11), Arrays.copyOfRange(requestFrame, 7, 11))) {
+ String requestInverterId = parseInverterId(requestFrame);
+ String responseInverterId = parseInverterId(responseFrame);
+
+ String message = String
+ .format("There was a mismatch between the request logger ID: %s and the response logger ID: %s. "
+ + "Make sure you are using the logger ID and not the inverter ID. "
+ + "If in doubt, try the one in the response", requestInverterId, responseInverterId);
+
+ throw new SolarmanAuthenticationException(message, requestInverterId, responseInverterId);
+ }
+
+ if (responseFrame[1] != (byte) 0x10 || responseFrame[2] != (byte) 0x45) {
+ throw new SolarmanProtocolException("Unexpected control code in error response frame");
+ }
+
+ int errorCode = responseFrame[25];
+
+ String message = switch (errorCode) {
+ case 0x01 -> "Error response frame: Illegal Function";
+ case 0x02 -> "Error response frame: Illegal Data Address";
+ case 0x03 -> "Error response frame: Illegal Data Value";
+ case 0x04 -> "Error response frame: Slave Device Failure";
+ default -> String.format("Error response frame: Unknown error code %02x", errorCode);
+ };
+ throw new SolarmanProtocolException(message);
+ }
+
+ private static String parseInverterId(byte[] requestFrame) {
+ byte[] inverterIdBytes = Arrays.copyOfRange(requestFrame, 7, 11);
+ int inverterIdInt = ByteBuffer.wrap(inverterIdBytes).order(ByteOrder.LITTLE_ENDIAN).getInt();
+ return String.valueOf(inverterIdInt & 0x00000000ffffffffL);
+ }
+}
diff --git a/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/modbus/exception/SolarmanAuthenticationException.java b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/modbus/exception/SolarmanAuthenticationException.java
new file mode 100644
index 00000000000..10442c9cb1e
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/modbus/exception/SolarmanAuthenticationException.java
@@ -0,0 +1,43 @@
+/**
+ * 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.solarman.internal.modbus.exception;
+
+import java.io.Serial;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * @author Catalin Sanda - Initial contribution
+ */
+@NonNullByDefault
+public class SolarmanAuthenticationException extends SolarmanException {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ private final String requestInverterId;
+ private final String responseInverterId;
+
+ public SolarmanAuthenticationException(String message, String requestInverterId, String responseInverterId) {
+ super(message);
+ this.requestInverterId = requestInverterId;
+ this.responseInverterId = responseInverterId;
+ }
+
+ public String getRequestInverterId() {
+ return requestInverterId;
+ }
+
+ public String getResponseInverterId() {
+ return responseInverterId;
+ }
+}
diff --git a/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/modbus/exception/SolarmanConnectionException.java b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/modbus/exception/SolarmanConnectionException.java
new file mode 100644
index 00000000000..e88d3e0e130
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/modbus/exception/SolarmanConnectionException.java
@@ -0,0 +1,34 @@
+/**
+ * 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.solarman.internal.modbus.exception;
+
+import java.io.Serial;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * @author Catalin Sanda - Initial contribution
+ */
+@NonNullByDefault
+public class SolarmanConnectionException extends SolarmanException {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ public SolarmanConnectionException(String message) {
+ super(message);
+ }
+
+ public SolarmanConnectionException(String message, Exception cause) {
+ super(message, cause);
+ }
+}
diff --git a/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/modbus/exception/SolarmanException.java b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/modbus/exception/SolarmanException.java
new file mode 100644
index 00000000000..06d530991c8
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/modbus/exception/SolarmanException.java
@@ -0,0 +1,34 @@
+/**
+ * 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.solarman.internal.modbus.exception;
+
+import java.io.Serial;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * @author Catalin Sanda - Initial contribution
+ */
+@NonNullByDefault
+public class SolarmanException extends Exception {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ public SolarmanException(String message) {
+ super(message);
+ }
+
+ public SolarmanException(String message, Exception cause) {
+ super(message, cause);
+ }
+}
diff --git a/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/modbus/exception/SolarmanProtocolException.java b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/modbus/exception/SolarmanProtocolException.java
new file mode 100644
index 00000000000..4a57fb52c9a
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/modbus/exception/SolarmanProtocolException.java
@@ -0,0 +1,30 @@
+/**
+ * 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.solarman.internal.modbus.exception;
+
+import java.io.Serial;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * @author Catalin Sanda - Initial contribution
+ */
+@NonNullByDefault
+public class SolarmanProtocolException extends SolarmanException {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ public SolarmanProtocolException(String message) {
+ super(message);
+ }
+}
diff --git a/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/typeprovider/ChannelUtils.java b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/typeprovider/ChannelUtils.java
new file mode 100644
index 00000000000..6a49b99adad
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/typeprovider/ChannelUtils.java
@@ -0,0 +1,142 @@
+/**
+ * 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.solarman.internal.typeprovider;
+
+import javax.measure.Unit;
+import javax.measure.quantity.Dimensionless;
+import javax.measure.quantity.ElectricCurrent;
+import javax.measure.quantity.ElectricPotential;
+import javax.measure.quantity.Energy;
+import javax.measure.quantity.Frequency;
+import javax.measure.quantity.Power;
+import javax.measure.quantity.Temperature;
+import javax.measure.quantity.Time;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.solarman.internal.SolarmanBindingConstants;
+import org.openhab.binding.solarman.internal.defmodel.ParameterItem;
+import org.openhab.core.library.CoreItemFactory;
+import org.openhab.core.library.unit.MetricPrefix;
+import org.openhab.core.library.unit.SIUnits;
+import org.openhab.core.library.unit.Units;
+import org.openhab.core.thing.type.ChannelTypeUID;
+
+/**
+ * The {@link ChannelUtils} class provides utility functions for handling channel types and units in the Solarman
+ * binding.
+ * It includes methods for determining item types, units of measure, and channel type IDs.
+ *
+ * @author Catalin Sanda - Initial contribution
+ */
+@NonNullByDefault
+public class ChannelUtils {
+
+ /**
+ * Determines the item type for a given parameter item.
+ *
+ * @param item The parameter item to determine the type for
+ * @return The item type as a string
+ */
+ public static String getItemType(ParameterItem item) {
+ @Nullable
+ Integer rule = item.getRule();
+
+ @Nullable
+ String uom = item.getUom();
+ if (uom == null) {
+ uom = "UNKN";
+ }
+
+ return switch (rule) {
+ case 5, 6, 7, 9 -> CoreItemFactory.STRING;
+ case 8 -> CoreItemFactory.DATETIME;
+ default -> {
+ yield computeNumberType(uom);
+ }
+ };
+ }
+
+ /**
+ * Computes the number type based on the unit of measure (UOM).
+ *
+ * @param uom The unit of measure as a string
+ * @return The number type as a string
+ */
+ private static String computeNumberType(String uom) {
+ return switch (uom.toUpperCase()) {
+ case "A" -> CoreItemFactory.NUMBER + ":" + ElectricCurrent.class.getSimpleName();
+ case "V" -> CoreItemFactory.NUMBER + ":" + ElectricPotential.class.getSimpleName();
+ case "°C" -> CoreItemFactory.NUMBER + ":" + Temperature.class.getSimpleName();
+ case "W", "KW", "VA", "KVA", "VAR", "KVAR" -> CoreItemFactory.NUMBER + ":" + Power.class.getSimpleName();
+ case "WH", "KWH" -> CoreItemFactory.NUMBER + ":" + Energy.class.getSimpleName();
+ case "S" -> CoreItemFactory.NUMBER + ":" + Time.class.getSimpleName();
+ case "HZ" -> CoreItemFactory.NUMBER + ":" + Frequency.class.getSimpleName();
+ case "%" -> CoreItemFactory.NUMBER + ":" + Dimensionless.class.getSimpleName();
+ default -> CoreItemFactory.NUMBER;
+ };
+ }
+
+ /**
+ * Retrieves the unit of measure (UOM) from a string definition.
+ *
+ * @param uom The unit of measure as a string
+ * @return The corresponding {@link Unit}, or null if not found
+ */
+ public static @Nullable Unit> getUnitFromDefinition(String uom) {
+ return switch (uom.toUpperCase()) {
+ case "A" -> Units.AMPERE;
+ case "V" -> Units.VOLT;
+ case "°C" -> SIUnits.CELSIUS;
+ case "W" -> Units.WATT;
+ case "KW" -> MetricPrefix.KILO(Units.WATT);
+ case "VA" -> Units.VOLT_AMPERE;
+ case "KVA" -> MetricPrefix.KILO(Units.VOLT_AMPERE);
+ case "VAR" -> Units.VAR;
+ case "KVAR" -> MetricPrefix.KILO(Units.VAR);
+ case "WH" -> Units.WATT_HOUR;
+ case "KWH" -> MetricPrefix.KILO(Units.WATT_HOUR);
+ case "S" -> Units.SECOND;
+ case "HZ" -> Units.HERTZ;
+ case "%" -> Units.PERCENT;
+ default -> null;
+ };
+ }
+
+ /**
+ * Escapes a name string by replacing specific characters with hyphens and converting to lowercase.
+ *
+ * @param name The name to escape
+ * @return The escaped name
+ */
+ public static String escapeName(String name) {
+ name = name.trim();
+ name = name.replace("+", "plus");
+ name = name.toLowerCase();
+ name = name.replaceAll("[ .()/\\\\&_]", "-");
+ return name;
+ }
+
+ /**
+ * Computes a channel type ID based on the inverter definition ID, group, and name.
+ *
+ * @param inverterDefinitionId The inverter definition ID
+ * @param group The group
+ * @param name The name
+ * @return The computed {@link ChannelTypeUID}
+ */
+ public static ChannelTypeUID computeChannelTypeId(String inverterDefinitionId, String group, String name) {
+ return new ChannelTypeUID(SolarmanBindingConstants.SOLARMAN_BINDING_ID,
+ String.format("%s-%s-%s", escapeName(inverterDefinitionId), escapeName(group), escapeName(name)));
+ }
+}
diff --git a/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/typeprovider/SolarmanChannelTypeProvider.java b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/typeprovider/SolarmanChannelTypeProvider.java
new file mode 100644
index 00000000000..fb27dd593b6
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/typeprovider/SolarmanChannelTypeProvider.java
@@ -0,0 +1,131 @@
+/**
+ * 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.solarman.internal.typeprovider;
+
+import static org.openhab.binding.solarman.internal.typeprovider.ChannelUtils.getItemType;
+
+import java.math.BigDecimal;
+import java.net.URI;
+import java.net.URL;
+import java.util.AbstractMap;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.solarman.internal.DefinitionParser;
+import org.openhab.binding.solarman.internal.defmodel.InverterDefinition;
+import org.openhab.binding.solarman.internal.defmodel.ParameterItem;
+import org.openhab.core.thing.type.ChannelType;
+import org.openhab.core.thing.type.ChannelTypeBuilder;
+import org.openhab.core.thing.type.ChannelTypeProvider;
+import org.openhab.core.thing.type.ChannelTypeUID;
+import org.openhab.core.thing.type.StateChannelTypeBuilder;
+import org.openhab.core.types.StateDescriptionFragmentBuilder;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Catalin Sanda - Initial contribution
+ */
+@Component(service = { ChannelTypeProvider.class, SolarmanChannelTypeProvider.class })
+@NonNullByDefault
+public class SolarmanChannelTypeProvider implements ChannelTypeProvider {
+ private final Logger logger = LoggerFactory.getLogger(SolarmanChannelTypeProvider.class);
+ private static final DefinitionParser DEFINITION_PARSER = new DefinitionParser();
+ private static final Pattern INVERTER_DEFINITION_PATTERN = Pattern.compile("/definitions/([^.]+)\\.yaml");
+ private final Map channelTypeMap = new ConcurrentHashMap<>();
+
+ @Activate
+ public SolarmanChannelTypeProvider(BundleContext bundleContext) {
+ Collections.list(bundleContext.getBundle().findEntries("/definitions", "*", false)).stream().map(URL::getFile)
+ .map(this::extractInverterDefinitionId).filter(Optional::isPresent).map(Optional::get)
+ .map(this::parseInverterDefinition).forEach(channelTypeMap::putAll);
+ }
+
+ private Map parseInverterDefinition(String inverterDefinitionId) {
+ InverterDefinition inverterDefinition = DEFINITION_PARSER.parseDefinition(inverterDefinitionId);
+
+ if (inverterDefinition == null) {
+ logger.warn("Unable to parse inverter definition");
+ return Collections.emptyMap();
+ }
+
+ return inverterDefinition.getParameters().stream()
+ .flatMap(parameter -> parameter.getItems().stream().map(item -> {
+ ChannelTypeUID channelTypeUID = ChannelUtils.computeChannelTypeId(inverterDefinitionId,
+ parameter.getGroup(), item.getName());
+ return new AbstractMap.SimpleEntry<>(channelTypeUID, buildChannelType(channelTypeUID, item));
+ })).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+ }
+
+ private Optional extractInverterDefinitionId(String file) {
+ return Stream.of(file).map(INVERTER_DEFINITION_PATTERN::matcher).filter(Matcher::matches)
+ .map(matcher -> matcher.group(1)).findFirst();
+ }
+
+ public Collection getChannelTypes(@Nullable Locale locale) {
+ return List.copyOf(this.channelTypeMap.values());
+ }
+
+ @Override
+ public @Nullable ChannelType getChannelType(ChannelTypeUID channelTypeUID, @Nullable Locale locale) {
+ return this.channelTypeMap.get(channelTypeUID);
+ }
+
+ public ChannelType buildChannelType(ChannelTypeUID channelTypeUID, ParameterItem item) {
+ String itemType = getItemType(item);
+
+ StateDescriptionFragmentBuilder stateDescriptionFragmentBuilder = StateDescriptionFragmentBuilder.create()
+ .withPattern(computePatternForItem(item)).withReadOnly(true);
+
+ StateChannelTypeBuilder stateChannelTypeBuilder = ChannelTypeBuilder
+ .state(channelTypeUID, item.getName(), itemType)
+ .withConfigDescriptionURI(URI.create("channel-type-config:solarman:dynamic-channel"))
+ .withDescription(String.format("%s %s", item.getName(), buildRegisterDescription(item)))
+ .withStateDescriptionFragment(stateDescriptionFragmentBuilder.build());
+
+ return stateChannelTypeBuilder.build();
+ }
+
+ private String computePatternForItem(ParameterItem item) {
+ long decimalPoints = 0;
+
+ BigDecimal scale = Objects.requireNonNullElse(item.getScale(), BigDecimal.ONE);
+ if (scale.compareTo(BigDecimal.ONE) < 0) {
+ decimalPoints = Math.abs(Math.round(Math.log10(scale.doubleValue())));
+ }
+
+ String uom = item.getUom();
+ String pattern = (decimalPoints > 0) ? "%." + decimalPoints + "f" : "%d";
+ return pattern + (uom != null && !uom.isBlank() ? " %unit%" : "");
+ }
+
+ private String buildRegisterDescription(ParameterItem item) {
+ return String.format("[%s]", item.getRegisters().stream().map(register -> String.format("0x%04X", register))
+ .collect(Collectors.joining(",")));
+ }
+}
diff --git a/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/updater/SolarmanChannelUpdater.java b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/updater/SolarmanChannelUpdater.java
new file mode 100644
index 00000000000..a9aa8ccd8d2
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/updater/SolarmanChannelUpdater.java
@@ -0,0 +1,238 @@
+/**
+ * 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.solarman.internal.updater;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import javax.measure.Unit;
+import javax.measure.format.MeasurementParseException;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.solarman.internal.defmodel.ParameterItem;
+import org.openhab.binding.solarman.internal.defmodel.Request;
+import org.openhab.binding.solarman.internal.modbus.SolarmanLoggerConnection;
+import org.openhab.binding.solarman.internal.modbus.SolarmanLoggerConnector;
+import org.openhab.binding.solarman.internal.modbus.SolarmanV5Protocol;
+import org.openhab.binding.solarman.internal.modbus.exception.SolarmanConnectionException;
+import org.openhab.binding.solarman.internal.modbus.exception.SolarmanException;
+import org.openhab.binding.solarman.internal.typeprovider.ChannelUtils;
+import org.openhab.binding.solarman.internal.util.StreamUtils;
+import org.openhab.core.library.types.DateTimeType;
+import org.openhab.core.library.types.DecimalType;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.types.StringType;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.types.State;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Catalin Sanda - Initial contribution
+ */
+@NonNullByDefault
+public class SolarmanChannelUpdater {
+ private final Logger logger = LoggerFactory.getLogger(SolarmanChannelUpdater.class);
+ private final StateUpdater stateUpdater;
+
+ public SolarmanChannelUpdater(StateUpdater stateUpdater) {
+ this.stateUpdater = stateUpdater;
+ }
+
+ public SolarmanProcessResult fetchDataFromLogger(List requests,
+ SolarmanLoggerConnector solarmanLoggerConnector, SolarmanV5Protocol solarmanV5Protocol,
+ Map paramToChannelMapping) {
+ try (SolarmanLoggerConnection solarmanLoggerConnection = solarmanLoggerConnector.createConnection()) {
+ logger.debug("Fetching data from logger");
+
+ if (!solarmanLoggerConnection.isConnected()) {
+ return SolarmanProcessResult.ofException(Request.NONE,
+ new SolarmanConnectionException("Unable to connect to logger"));
+ }
+
+ SolarmanProcessResult solarmanProcessResult = requests.stream().map(request -> {
+ try {
+ return SolarmanProcessResult.ofValue(request,
+ solarmanV5Protocol.readRegisters(solarmanLoggerConnection,
+ (byte) request.getMbFunctioncode().intValue(), request.getStart(),
+ request.getEnd()));
+ } catch (SolarmanException e) {
+ return SolarmanProcessResult.ofException(request, e);
+ }
+ }).reduce(new SolarmanProcessResult(), SolarmanProcessResult::merge);
+
+ if (solarmanProcessResult.hasSuccessfulResponses()) {
+ updateChannelsForReadRegisters(paramToChannelMapping, solarmanProcessResult.getReadRegistersMap());
+ }
+ return solarmanProcessResult;
+ }
+ }
+
+ private void updateChannelsForReadRegisters(Map paramToChannelMapping,
+ Map readRegistersMap) {
+ paramToChannelMapping.forEach((parameterItem, channelUID) -> {
+ List registers = parameterItem.getRegisters();
+ if (readRegistersMap.keySet().containsAll(registers)) {
+ switch (parameterItem.getRule()) {
+ case 1, 3 -> updateChannelWithNumericValue(parameterItem, channelUID, registers, readRegistersMap,
+ ValueType.UNSIGNED);
+ case 2, 4 -> updateChannelWithNumericValue(parameterItem, channelUID, registers, readRegistersMap,
+ ValueType.SIGNED);
+ case 5 -> updateChannelWithStringValue(channelUID, registers, readRegistersMap);
+ case 6 -> updateChannelWithRawValue(parameterItem, channelUID, registers, readRegistersMap);
+ case 7 -> updateChannelWithVersion(channelUID, registers, readRegistersMap);
+ case 8 -> updateChannelWithDateTime(channelUID, registers, readRegistersMap);
+ case 9 -> updateChannelWithTime(channelUID, registers, readRegistersMap);
+ }
+ } else {
+ logger.warn("Unable to update channel {} because its registers were not read", channelUID.getId());
+ }
+ });
+ }
+
+ private void updateChannelWithTime(ChannelUID channelUID, List registers,
+ Map readRegistersMap) {
+ String stringValue = registers.stream().map(readRegistersMap::get).map(v -> ByteBuffer.wrap(v).getShort())
+ .map(rawVal -> String.format("%02d", rawVal / 100) + ":" + String.format("%02d", rawVal % 100))
+ .collect(Collectors.joining());
+
+ stateUpdater.updateState(channelUID, new StringType(stringValue));
+ }
+
+ private void updateChannelWithDateTime(ChannelUID channelUID, List registers,
+ Map readRegistersMap) {
+ String stringValue = StreamUtils.zip(IntStream.range(0, registers.size()).boxed(),
+ registers.stream().map(readRegistersMap::get).map(v -> ByteBuffer.wrap(v).getShort()),
+ StreamUtils.Tuple::new).map(t -> {
+ int index = t.a();
+ short rawVal = t.b();
+
+ return switch (index) {
+ case 0 -> (rawVal >> 8) + "/" + (rawVal & 0xFF) + "/";
+ case 1 -> (rawVal >> 8) + " " + (rawVal & 0xFF) + ":";
+ case 2 -> (rawVal >> 8) + ":" + (rawVal & 0xFF);
+ default -> (rawVal >> 8) + "" + (rawVal & 0xFF);
+ };
+ }).collect(Collectors.joining());
+
+ try {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yy/M/d H:m:s");
+ LocalDateTime dateTime = LocalDateTime.parse(stringValue, formatter);
+
+ stateUpdater.updateState(channelUID, new DateTimeType(dateTime.atZone(ZoneId.systemDefault())));
+ } catch (DateTimeParseException e) {
+ logger.debug("Unable to parse string date {} to a DateTime object", stringValue);
+ }
+ }
+
+ private void updateChannelWithVersion(ChannelUID channelUID, List registers,
+ Map readRegistersMap) {
+ String stringValue = registers.stream().map(readRegistersMap::get).map(v -> ByteBuffer.wrap(v).getShort())
+ .map(rawVal -> (rawVal >> 12) + "." + ((rawVal >> 8) & 0x0F) + "." + ((rawVal >> 4) & 0x0F) + "."
+ + (rawVal & 0x0F))
+ .collect(Collectors.joining());
+
+ stateUpdater.updateState(channelUID, new StringType(stringValue));
+ }
+
+ private void updateChannelWithStringValue(ChannelUID channelUID, List registers,
+ Map readRegistersMap) {
+ String stringValue = registers.stream().map(readRegistersMap::get).reduce(new StringBuilder(), (acc, val) -> {
+ short shortValue = ByteBuffer.wrap(val).order(ByteOrder.BIG_ENDIAN).getShort();
+ return acc.append((char) (shortValue >> 8)).append((char) (shortValue & 0xFF));
+ }, StringBuilder::append).toString();
+
+ stateUpdater.updateState(channelUID, new StringType(stringValue));
+ }
+
+ private void updateChannelWithNumericValue(ParameterItem parameterItem, ChannelUID channelUID,
+ List registers, Map readRegistersMap, ValueType valueType) {
+ BigInteger value = extractNumericValue(registers, readRegistersMap, valueType);
+ BigDecimal convertedValue = convertNumericValue(value, parameterItem.getOffset(), parameterItem.getScale());
+ String uom = Objects.requireNonNullElse(parameterItem.getUom(), "");
+
+ State state;
+ if (!uom.isBlank()) {
+ try {
+ Unit> unitFromDefinition = ChannelUtils.getUnitFromDefinition(uom);
+ if (unitFromDefinition != null) {
+ state = new QuantityType<>(convertedValue, unitFromDefinition);
+ } else {
+ logger.debug("Unable to parse unit: {}", uom);
+ state = new DecimalType(convertedValue);
+ }
+ } catch (MeasurementParseException e) {
+ state = new DecimalType(convertedValue);
+ }
+ } else {
+ state = new DecimalType(convertedValue);
+ }
+ stateUpdater.updateState(channelUID, state);
+ }
+
+ private void updateChannelWithRawValue(ParameterItem parameterItem, ChannelUID channelUID, List registers,
+ Map readRegistersMap) {
+ String hexString = String.format("[%s]",
+ reversed(registers).stream().map(readRegistersMap::get).map(
+ val -> String.format("0x%02X", ByteBuffer.wrap(val).order(ByteOrder.BIG_ENDIAN).getShort()))
+ .collect(Collectors.joining(",")));
+
+ stateUpdater.updateState(channelUID, new StringType(hexString));
+ }
+
+ private BigDecimal convertNumericValue(BigInteger value, @Nullable BigDecimal offset, @Nullable BigDecimal scale) {
+ return new BigDecimal(value).subtract(offset != null ? offset : BigDecimal.ZERO)
+ .multiply(scale != null ? scale : BigDecimal.ONE);
+ }
+
+ private BigInteger extractNumericValue(List registers, Map readRegistersMap,
+ ValueType valueType) {
+ return reversed(registers)
+ .stream().map(readRegistersMap::get).reduce(
+ BigInteger.ZERO, (acc,
+ val) -> acc.shiftLeft(Short.SIZE)
+ .add(BigInteger.valueOf(ByteBuffer.wrap(val).getShort()
+ & (valueType == ValueType.UNSIGNED ? 0xFFFF : 0xFFFFFFFF))),
+ BigInteger::add);
+ }
+
+ private enum ValueType {
+ UNSIGNED,
+ SIGNED
+ }
+
+ @FunctionalInterface
+ public interface StateUpdater {
+ void updateState(ChannelUID channelUID, State state);
+ }
+
+ private List reversed(List initialList) {
+ List reversedList = new ArrayList<>(initialList);
+ Collections.reverse(reversedList);
+ return reversedList;
+ }
+}
diff --git a/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/updater/SolarmanProcessResult.java b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/updater/SolarmanProcessResult.java
new file mode 100644
index 00000000000..5bc66d9154b
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/updater/SolarmanProcessResult.java
@@ -0,0 +1,88 @@
+/**
+ * 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.solarman.internal.updater;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.solarman.internal.defmodel.Request;
+import org.openhab.binding.solarman.internal.modbus.exception.SolarmanException;
+
+/**
+ * @author Catalin Sanda - Initial contribution
+ */
+@NonNullByDefault
+public class SolarmanProcessResult {
+ private final Map> successfulRequestMap;
+ private final Map exceptionRequestMap;
+
+ public SolarmanProcessResult() {
+ this(Collections.emptyMap(), Collections.emptyMap());
+ }
+
+ private SolarmanProcessResult(Map> successfulRequestMap,
+ Map exceptionRequestMap) {
+ this.successfulRequestMap = successfulRequestMap;
+ this.exceptionRequestMap = exceptionRequestMap;
+ }
+
+ public static SolarmanProcessResult merge(SolarmanProcessResult result1, SolarmanProcessResult result2) {
+ return new SolarmanProcessResult(mergeMaps(result1.successfulRequestMap, result2.successfulRequestMap),
+ mergeMaps(result1.exceptionRequestMap, result2.exceptionRequestMap));
+ }
+
+ public static SolarmanProcessResult ofValue(Request request, Map readRegisters) {
+ return new SolarmanProcessResult(Collections.singletonMap(request, readRegisters), new HashMap<>());
+ }
+
+ public static SolarmanProcessResult ofException(Request request, SolarmanException solarmanException) {
+ return new SolarmanProcessResult(new HashMap<>(), Collections.singletonMap(request, solarmanException));
+ }
+
+ public boolean hasSuccessfulResponses() {
+ return !successfulRequestMap.isEmpty();
+ }
+
+ public Map getReadRegistersMap() {
+ return successfulRequestMap.values().stream().reduce(new HashMap<>(), SolarmanProcessResult::mergeMaps);
+ }
+
+ private static Map mergeMaps(Map map1, Map map2) {
+ return Stream.concat(map1.entrySet().stream(), map2.entrySet().stream())
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v1));
+ }
+
+ @Override
+ public String toString() {
+ if (!successfulRequestMap.isEmpty() && exceptionRequestMap.isEmpty()) {
+ return String.format("Successfully executed %d requests", successfulRequestMap.size());
+ } else if (successfulRequestMap.isEmpty() && !exceptionRequestMap.isEmpty()) {
+ return String.format("Error fetching data from logger, here are the errors:\n%s",
+ buildErrorReport(exceptionRequestMap));
+ } else if (!successfulRequestMap.isEmpty()) {
+ return String.format("Successfully executed %d requests, but %d requests failed with:\n%s",
+ successfulRequestMap.size(), exceptionRequestMap.size(), buildErrorReport(exceptionRequestMap));
+ } else {
+ return "Empty SolarmanProcessResult";
+ }
+ }
+
+ private String buildErrorReport(Map exceptionRequestMap) {
+ return exceptionRequestMap.entrySet().stream().map(entry -> String.format("\tRequest %s returned error: %s\n",
+ entry.getKey().toString(), entry.getValue().getMessage())).reduce("", String::concat);
+ }
+}
diff --git a/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/util/StreamUtils.java b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/util/StreamUtils.java
new file mode 100644
index 00000000000..91bae6c7e52
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/java/org/openhab/binding/solarman/internal/util/StreamUtils.java
@@ -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.solarman.internal.util;
+
+import java.util.Iterator;
+import java.util.Objects;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.function.BiFunction;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Utility class for Stream operations.
+ *
+ * @author Catalin Sanda - Initial contribution
+ */
+@NonNullByDefault
+public class StreamUtils {
+
+ /**
+ * Zips two streams into one by applying a zipper function to each pair of elements.
+ *
+ * @param The type of the first stream elements
+ * @param The type of the second stream elements
+ * @param The type of the resulting stream elements
+ * @param a The first stream to be zipped
+ * @param b The second stream to be zipped
+ * @param zipper The function to apply to each pair of elements
+ * @return A stream of zipped elements
+ */
+ public static Stream zip(Stream extends A> a, Stream extends B> b,
+ BiFunction super A, ? super B, ? extends C> zipper) {
+ Objects.requireNonNull(zipper);
+ Spliterator extends A> aSpliterator = Objects.requireNonNull(a).spliterator();
+ Spliterator extends B> bSpliterator = Objects.requireNonNull(b).spliterator();
+
+ // Zipping looses DISTINCT and SORTED characteristics
+ int characteristics = aSpliterator.characteristics() & bSpliterator.characteristics()
+ & ~(Spliterator.DISTINCT | Spliterator.SORTED);
+
+ long zipSize = ((characteristics & Spliterator.SIZED) != 0)
+ ? Math.min(aSpliterator.getExactSizeIfKnown(), bSpliterator.getExactSizeIfKnown())
+ : -1;
+
+ Iterator aIterator = Spliterators.iterator(aSpliterator);
+ Iterator bIterator = Spliterators.iterator(bSpliterator);
+ Iterator cIterator = new Iterator() {
+ @Override
+ public boolean hasNext() {
+ return aIterator.hasNext() && bIterator.hasNext();
+ }
+
+ @Override
+ public C next() {
+ return zipper.apply(aIterator.next(), bIterator.next());
+ }
+ };
+
+ Spliterator split = Spliterators.spliterator(cIterator, zipSize, characteristics);
+ return (a.isParallel() || b.isParallel()) ? StreamSupport.stream(split, true)
+ : StreamSupport.stream(split, false);
+ }
+
+ /**
+ * A tuple class to hold two related objects.
+ *
+ * @param The type of the first object
+ * @param The type of the second object
+ */
+ public record Tuple (A a, B b) {
+ }
+}
diff --git a/bundles/org.openhab.binding.solarman/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.solarman/src/main/resources/OH-INF/addon/addon.xml
new file mode 100644
index 00000000000..44bc7a08c15
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/resources/OH-INF/addon/addon.xml
@@ -0,0 +1,10 @@
+
+
+
+ binding
+ Solarman Logger Binding
+ This is the binding for Solarman Logger
+ local
+
diff --git a/bundles/org.openhab.binding.solarman/src/main/resources/OH-INF/config/datetime-channel-config.xml b/bundles/org.openhab.binding.solarman/src/main/resources/OH-INF/config/datetime-channel-config.xml
new file mode 100644
index 00000000000..435e457bf8e
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/resources/OH-INF/config/datetime-channel-config.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+ The unit of measurement used for this channel
+ true
+
+
+
+ The scaling factor, the final value will be scaled by this
+ true
+
+
+
+ The type of measurement. See explanation for possible values
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The offset subtracted from the measurement
+ true
+
+
+
+ Comma separated list of registers to read for the measurement
+ true
+
+
+
+
diff --git a/bundles/org.openhab.binding.solarman/src/main/resources/OH-INF/config/dynamic-channel-config.xml b/bundles/org.openhab.binding.solarman/src/main/resources/OH-INF/config/dynamic-channel-config.xml
new file mode 100644
index 00000000000..326437b10c8
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/resources/OH-INF/config/dynamic-channel-config.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+ The unit of measurement used for this channel
+ true
+
+
+
+ The scaling factor, the final value will be scaled by this
+ true
+
+
+
+ The type of measurement. See explanation for possible values
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The offset subtracted from the measurement
+ true
+
+
+
+ Comma separated list of registers to read for the measurement
+ true
+
+
+
+
diff --git a/bundles/org.openhab.binding.solarman/src/main/resources/OH-INF/config/number-channel-config.xml b/bundles/org.openhab.binding.solarman/src/main/resources/OH-INF/config/number-channel-config.xml
new file mode 100644
index 00000000000..593853d90a5
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/resources/OH-INF/config/number-channel-config.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+ The unit of measurement used for this channel
+ true
+
+
+
+ The scaling factor, the final value will be scaled by this
+ true
+
+
+
+ The type of measurement. See explanation for possible values
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The offset subtracted from the measurement
+ true
+
+
+
+ Comma separated list of registers to read for the measurement
+ true
+
+
+
+
diff --git a/bundles/org.openhab.binding.solarman/src/main/resources/OH-INF/config/string-channel-config.xml b/bundles/org.openhab.binding.solarman/src/main/resources/OH-INF/config/string-channel-config.xml
new file mode 100644
index 00000000000..92ba342efe1
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/resources/OH-INF/config/string-channel-config.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+ The unit of measurement used for this channel
+ true
+
+
+
+ The scaling factor, the final value will be scaled by this
+ true
+
+
+
+ The type of measurement. See explanation for possible values
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The offset subtracted from the measurement
+ true
+
+
+
+ Comma separated list of registers to read for the measurement
+ true
+
+
+
+
diff --git a/bundles/org.openhab.binding.solarman/src/main/resources/OH-INF/i18n/solarman.properties b/bundles/org.openhab.binding.solarman/src/main/resources/OH-INF/i18n/solarman.properties
new file mode 100644
index 00000000000..58f7dfd6e41
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/resources/OH-INF/i18n/solarman.properties
@@ -0,0 +1,123 @@
+# add-on
+
+addon.solarman.name = Solarman Logger Binding
+addon.solarman.description = This is the binding for Solarman Logger
+
+# thing types
+
+thing-type.solarman.logger.label = Solarman Logger
+thing-type.solarman.logger.description = This thing allows communication with Solarman (IGEN-Tech) v5 based solar inverter data loggers over the local network. Compatible with inverters from manufacturers such as Deye, Sofar, Solis, ZCS Azzurro, and KStar.
+
+# thing types config
+
+thing-type.config.solarman.logger.additionalRequests.label = Additional Requests
+thing-type.config.solarman.logger.additionalRequests.description = Additional requests besides the ones defined in the inverter definition. Format is mb_functioncode1:start1-end1, mb_functioncode2:start2-end2,... Example 0x03:0x0000-0x0100,0x03:0x0200-0x0300
+thing-type.config.solarman.logger.hostname.label = Hostname
+thing-type.config.solarman.logger.hostname.description = Hostname or IP address of the Solarman logger.
+thing-type.config.solarman.logger.inverterType.label = Inverter Type
+thing-type.config.solarman.logger.inverterType.description = The type of inverter connected to the logger (default deye_sg04lp3).
+thing-type.config.solarman.logger.inverterType.option.deye_2mppt = DEYE Microinverter with 2 MPPT Trackers (deye_2mppt)
+thing-type.config.solarman.logger.inverterType.option.deye_4mppt = DEYE Microinverter with 4 MPPT Trackers (deye_4mppt)
+thing-type.config.solarman.logger.inverterType.option.deye_hybrid = Generic DEYE/Sunsynk/SolArk Hybrid inverters (deye_hybrid)
+thing-type.config.solarman.logger.inverterType.option.deye_sg04lp3 = DEYE/Sunsynk/SolArk Hybrid 8/12K-SG04LP3 (deye_sg04lp3)
+thing-type.config.solarman.logger.inverterType.option.deye_string = Generic DEYE/Sunsynk/SolArk String inverters (deye_string)
+thing-type.config.solarman.logger.inverterType.option.kstar_hybrid = KSTAR Hybrid Inverter (kstar_hybrid)
+thing-type.config.solarman.logger.inverterType.option.sofar_g3hyd = SOFAR Hybrid Three-Phase Inverter (sofar_g3hyd)
+thing-type.config.solarman.logger.inverterType.option.sofar_hyd3k-6k-es = SOFAR Hybrid Single-Phase Inverter (sofar_hyd3k-6k-es)
+thing-type.config.solarman.logger.inverterType.option.sofar_lsw3 = SOFAR Inverters (sofar_lsw3)
+thing-type.config.solarman.logger.inverterType.option.sofar_wifikit = SOFAR WifiKit (sofar_wifikit)
+thing-type.config.solarman.logger.inverterType.option.solis_1p8k-5g = SOLIS 1P8K-5G (solis_1p8k-5g)
+thing-type.config.solarman.logger.inverterType.option.solis_hybrid = SOLIS Hybrid Inverter (solis_hybrid)
+thing-type.config.solarman.logger.inverterType.option.zcs_azzurro-ktl-v3 = ZCS Azzurro KTL-V3 Inverters (zcs_azzurro-ktl-v3)
+thing-type.config.solarman.logger.port.label = Port
+thing-type.config.solarman.logger.port.description = Port of the Solarman logger (default 8899).
+thing-type.config.solarman.logger.refreshInterval.label = Refresh Interval
+thing-type.config.solarman.logger.refreshInterval.description = Interval to query the logger (default 60).
+thing-type.config.solarman.logger.serialNumber.label = Serial Number
+thing-type.config.solarman.logger.serialNumber.description = Serial number of the Solarman logger.
+
+# channel types
+
+channel-type.solarman.datetime.label = Datetime Value
+channel-type.solarman.dynamic.label = Dynamic Channel
+channel-type.solarman.number.label = Number Value
+channel-type.solarman.string.label = Text Value
+
+# channel types config
+
+channel-type-config.config.solarman.datetime-channel.offset.label = Offset
+channel-type-config.config.solarman.datetime-channel.offset.description = The offset subtracted from the measurement
+channel-type-config.config.solarman.datetime-channel.registers.label = Registers
+channel-type-config.config.solarman.datetime-channel.registers.description = Comma separated list of registers to read for the measurement
+channel-type-config.config.solarman.datetime-channel.rule.label = Rule
+channel-type-config.config.solarman.datetime-channel.rule.description = The type of measurement. See explanation for possible values
+channel-type-config.config.solarman.datetime-channel.rule.option.1 = Unsigned Short
+channel-type-config.config.solarman.datetime-channel.rule.option.2 = Signed Short
+channel-type-config.config.solarman.datetime-channel.rule.option.3 = Unsigned Integer
+channel-type-config.config.solarman.datetime-channel.rule.option.4 = Signed Integer
+channel-type-config.config.solarman.datetime-channel.rule.option.5 = Text
+channel-type-config.config.solarman.datetime-channel.rule.option.6 = Bytes
+channel-type-config.config.solarman.datetime-channel.rule.option.7 = Version
+channel-type-config.config.solarman.datetime-channel.rule.option.8 = Date Time
+channel-type-config.config.solarman.datetime-channel.rule.option.9 = Time
+channel-type-config.config.solarman.datetime-channel.scale.label = Scale
+channel-type-config.config.solarman.datetime-channel.scale.description = The scaling factor, the final value will be scaled by this
+channel-type-config.config.solarman.datetime-channel.uom.label = Unit of Measurement
+channel-type-config.config.solarman.datetime-channel.uom.description = The unit of measurement used for this channel
+channel-type-config.config.solarman.dynamic-channel.offset.label = Offset
+channel-type-config.config.solarman.dynamic-channel.offset.description = The offset subtracted from the measurement
+channel-type-config.config.solarman.dynamic-channel.registers.label = Registers
+channel-type-config.config.solarman.dynamic-channel.registers.description = Comma separated list of registers to read for the measurement
+channel-type-config.config.solarman.dynamic-channel.rule.label = Rule
+channel-type-config.config.solarman.dynamic-channel.rule.description = The type of measurement. See explanation for possible values
+channel-type-config.config.solarman.dynamic-channel.rule.option.1 = Unsigned Short
+channel-type-config.config.solarman.dynamic-channel.rule.option.2 = Signed Short
+channel-type-config.config.solarman.dynamic-channel.rule.option.3 = Unsigned Integer
+channel-type-config.config.solarman.dynamic-channel.rule.option.4 = Signed Integer
+channel-type-config.config.solarman.dynamic-channel.rule.option.5 = Text
+channel-type-config.config.solarman.dynamic-channel.rule.option.6 = Bytes
+channel-type-config.config.solarman.dynamic-channel.rule.option.7 = Version
+channel-type-config.config.solarman.dynamic-channel.rule.option.8 = Date Time
+channel-type-config.config.solarman.dynamic-channel.rule.option.9 = Time
+channel-type-config.config.solarman.dynamic-channel.scale.label = Scale
+channel-type-config.config.solarman.dynamic-channel.scale.description = The scaling factor, the final value will be scaled by this
+channel-type-config.config.solarman.dynamic-channel.uom.label = Unit of Measurement
+channel-type-config.config.solarman.dynamic-channel.uom.description = The unit of measurement used for this channel
+channel-type-config.config.solarman.number-channel.offset.label = Offset
+channel-type-config.config.solarman.number-channel.offset.description = The offset subtracted from the measurement
+channel-type-config.config.solarman.number-channel.registers.label = Registers
+channel-type-config.config.solarman.number-channel.registers.description = Comma separated list of registers to read for the measurement
+channel-type-config.config.solarman.number-channel.rule.label = Rule
+channel-type-config.config.solarman.number-channel.rule.description = The type of measurement. See explanation for possible values
+channel-type-config.config.solarman.number-channel.rule.option.1 = Unsigned Short
+channel-type-config.config.solarman.number-channel.rule.option.2 = Signed Short
+channel-type-config.config.solarman.number-channel.rule.option.3 = Unsigned Integer
+channel-type-config.config.solarman.number-channel.rule.option.4 = Signed Integer
+channel-type-config.config.solarman.number-channel.rule.option.5 = Text
+channel-type-config.config.solarman.number-channel.rule.option.6 = Bytes
+channel-type-config.config.solarman.number-channel.rule.option.7 = Version
+channel-type-config.config.solarman.number-channel.rule.option.8 = Date Time
+channel-type-config.config.solarman.number-channel.rule.option.9 = Time
+channel-type-config.config.solarman.number-channel.scale.label = Scale
+channel-type-config.config.solarman.number-channel.scale.description = The scaling factor, the final value will be scaled by this
+channel-type-config.config.solarman.number-channel.uom.label = Unit of Measurement
+channel-type-config.config.solarman.number-channel.uom.description = The unit of measurement used for this channel
+channel-type-config.config.solarman.string-channel.offset.label = Offset
+channel-type-config.config.solarman.string-channel.offset.description = The offset subtracted from the measurement
+channel-type-config.config.solarman.string-channel.registers.label = Registers
+channel-type-config.config.solarman.string-channel.registers.description = Comma separated list of registers to read for the measurement
+channel-type-config.config.solarman.string-channel.rule.label = Rule
+channel-type-config.config.solarman.string-channel.rule.description = The type of measurement. See explanation for possible values
+channel-type-config.config.solarman.string-channel.rule.option.1 = Unsigned Short
+channel-type-config.config.solarman.string-channel.rule.option.2 = Signed Short
+channel-type-config.config.solarman.string-channel.rule.option.3 = Unsigned Integer
+channel-type-config.config.solarman.string-channel.rule.option.4 = Signed Integer
+channel-type-config.config.solarman.string-channel.rule.option.5 = Text
+channel-type-config.config.solarman.string-channel.rule.option.6 = Bytes
+channel-type-config.config.solarman.string-channel.rule.option.7 = Version
+channel-type-config.config.solarman.string-channel.rule.option.8 = Date Time
+channel-type-config.config.solarman.string-channel.rule.option.9 = Time
+channel-type-config.config.solarman.string-channel.scale.label = Scale
+channel-type-config.config.solarman.string-channel.scale.description = The scaling factor, the final value will be scaled by this
+channel-type-config.config.solarman.string-channel.uom.label = Unit of Measurement
+channel-type-config.config.solarman.string-channel.uom.description = The unit of measurement used for this channel
diff --git a/bundles/org.openhab.binding.solarman/src/main/resources/OH-INF/thing/channels.xml b/bundles/org.openhab.binding.solarman/src/main/resources/OH-INF/thing/channels.xml
new file mode 100644
index 00000000000..b8e1d752fe7
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/resources/OH-INF/thing/channels.xml
@@ -0,0 +1,31 @@
+
+
+
+
+ String
+
+
+
+
+
+ Number
+
+
+
+
+
+ DateTime
+
+
+
+
+
+ String
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.solarman/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.solarman/src/main/resources/OH-INF/thing/thing-types.xml
new file mode 100644
index 00000000000..4db7f770a2a
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/resources/OH-INF/thing/thing-types.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+ This thing allows communication with Solarman (IGEN-Tech) v5 based solar inverter data loggers over the
+ local network. Compatible with inverters from manufacturers such as Deye, Sofar, Solis, ZCS Azzurro, and KStar.
+
+
+
+ network-address
+
+ Hostname or IP address of the Solarman logger.
+ false
+
+
+
+ Port of the Solarman logger (default 8899).
+ 8899
+ true
+
+
+
+ The type of inverter connected to the logger (default deye_sg04lp3).
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Serial number of the Solarman logger.
+ false
+
+
+
+ Interval to query the logger (default 60).
+ 60
+ true
+
+
+
+ Additional requests besides the ones defined in the inverter definition.
+ Format is
+ mb_functioncode1:start1-end1, mb_functioncode2:start2-end2,...
+ Example 0x03:0x0000-0x0100,0x03:0x0200-0x0300
+
+ true
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.solarman/src/main/resources/definitions/deye_2mppt.yaml b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/deye_2mppt.yaml
new file mode 100644
index 00000000000..5ce0bd68604
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/deye_2mppt.yaml
@@ -0,0 +1,137 @@
+# First version : 22.2.2023
+# Microinverter SUN600G3 (DEYE/VESDAS)
+# 2x MPPT, 2x inverter
+# 1x Logger, 2x Module,
+
+requests:
+ - start: 0x0003
+ end: 0x0080
+ mb_functioncode: 0x03
+
+parameters:
+ - group: solar
+ items:
+ - name: "PV1 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x006D]
+ icon: 'mdi:solar-power'
+
+ - name: "PV2 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x006F]
+ icon: 'mdi:solar-power'
+
+ - name: "PV1 Current"
+ class: "current"
+ uom: "A"
+ scale: 0.1
+ rule: 1
+ registers: [0x006E]
+ icon: 'mdi:solar-power'
+
+ - name: "PV2 Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.1
+ rule: 1
+ registers: [0x0070]
+ icon: 'mdi:solar-power'
+
+ - name: "Daily Production"
+ class: "energy"
+ state_class: "total"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [0x003C]
+ icon: 'mdi:solar-power'
+
+ - name: "Total Production"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [0x003F,0x0040]
+ icon: 'mdi:solar-power'
+ validation:
+ min: 0.1
+
+ - group: Grid
+ items:
+ - name: "AC Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x0049]
+ icon: 'mdi:transmission-tower'
+
+ - name: "AC Output Frequency"
+ class: "frequency"
+ state_class: "measurement"
+ uom: "Hz"
+ scale: 0.01
+ rule: 1
+ registers: [0x004F]
+ icon: 'mdi:home-lightning-bolt'
+
+ - group: Inverter
+ items:
+ - name: "Running Status"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x003B]
+ isstr: true
+ lookup:
+ - key: 0
+ value: "Stand-by"
+ - key: 1
+ value: "Self-check"
+ - key: 2
+ value: "Normal"
+ - key: 3
+ value: "Warning"
+ - key: 4
+ value: "Fault"
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Total AC Output Power (Active)"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 0.1
+ rule: 3
+ registers: [0x0056, 0x0057]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Radiator Temperature"
+ class: "temperature"
+ uom: "°C"
+ state_class: "measurement"
+ scale: 0.01
+ rule: 1
+ offset: 1000
+ registers: [0x005a]
+
+ - name: "Inverter ID"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 5
+ registers: [0x0003,0x0004,0x0005,0x0006,0x0007]
+ isstr: true
diff --git a/bundles/org.openhab.binding.solarman/src/main/resources/definitions/deye_4mppt.yaml b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/deye_4mppt.yaml
new file mode 100644
index 00000000000..2b84b178230
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/deye_4mppt.yaml
@@ -0,0 +1,470 @@
+#
+# Borrowed form https://github.com/StephanJoubert/home_assistant_solarman/
+# Additional info from https://github.com/kbialek/deye-inverter-mqtt/blob/19ace123339beec7a574b983f631309f8d285883/deye_sensor.py
+#
+# First version : 22.2.2023
+# Microinverter SUN600G3 (DEYE/VESDAS)
+# 2x MPPT, 2x inverter
+# 1x Logger, 2x Module,
+# Added info for 4x MPPT Microinverters on 2023-06-23
+
+requests:
+ - start: 0x0003
+ end: 0x0080
+ mb_functioncode: 0x03
+
+parameters:
+ - group: solar
+ items:
+ - name: "PV1 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x006D]
+ icon: 'mdi:solar-power'
+
+ - name: "PV2 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x006F]
+ icon: 'mdi:solar-power'
+
+ - name: "PV3 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x0071]
+ icon: 'mdi:solar-power'
+
+ - name: "PV4 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x0073]
+ icon: 'mdi:solar-power'
+
+ - name: "PV1 Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.1
+ rule: 1
+ registers: [0x006E]
+ icon: 'mdi:solar-power'
+
+ - name: "PV2 Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.1
+ rule: 1
+ registers: [0x0070]
+ icon: 'mdi:solar-power'
+
+ - name: "PV3 Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.1
+ rule: 1
+ registers: [0x0072]
+ icon: 'mdi:solar-power'
+
+ - name: "PV4 Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.1
+ rule: 1
+ registers: [0x0074]
+ icon: 'mdi:solar-power'
+
+ - name: "Daily Production"
+ class: "energy"
+ state_class: "total"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [0x003C]
+ icon: 'mdi:solar-power'
+
+ - name: "Daily Production 1"
+ class: "energy"
+ state_class: "total"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [0x0041]
+ icon: 'mdi:solar-power'
+
+ - name: "Daily Production 2"
+ class: "energy"
+ state_class: "total"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [0x0042]
+ icon: 'mdi:solar-power'
+
+ - name: "Daily Production 3"
+ class: "energy"
+ state_class: "total"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [0x0043]
+ icon: 'mdi:solar-power'
+
+ - name: "Daily Production 4"
+ class: "energy"
+ state_class: "total"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [0x0044]
+ icon: 'mdi:solar-power'
+
+ - name: "Total Production"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [0x003F,0x0040]
+ icon: 'mdi:solar-power'
+ validation:
+ min: 0.1
+
+ - name: "Total Production 1"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [0x0045]
+ icon: 'mdi:solar-power'
+
+ - name: "Total Production 2"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [0x0047]
+ icon: 'mdi:solar-power'
+
+ - name: "Total Production 3"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [0x0046]
+ icon: 'mdi:solar-power'
+
+ - name: "Total Production 4"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [0x0048]
+ icon: 'mdi:solar-power'
+
+ - name: "Active Power Regulations"
+ class: ""
+ state_class: ""
+ uom: "%"
+ scale: 1
+ rule: 1
+ registers: [0x0028]
+ icon: 'mdi:solar-power'
+
+ - group: Grid
+ items:
+ - name: "AC Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x0049]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Grid Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.1
+ rule: 2
+ registers: [0x004C]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "AC Output Frequency"
+ class: "frequency"
+ state_class: "measurement"
+ uom: "Hz"
+ scale: 0.01
+ rule: 1
+ registers: [0x004F]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Grid Voltage Upp Limit"
+ class: "voltage"
+ state_class: ""
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x001B]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Grid Voltage Lower Limit"
+ class: "voltage"
+ state_class: ""
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x001C]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Grid Frequency Upper Limit"
+ class: "frequency"
+ state_class: ""
+ uom: "Hz"
+ scale: 0.01
+ rule: 1
+ registers: [0x001D]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Grid Frequency Lower Limit"
+ class: "frequency"
+ state_class: ""
+ uom: "Hz"
+ scale: 0.01
+ rule: 1
+ registers: [0x001E]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Overfrequency And Load Reduction Starting Point"
+ class: "frequency"
+ state_class: ""
+ uom: "Hz"
+ scale: 0.01
+ rule: 1
+ registers: [0x0022]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Overfrequency And Load Reduction Percentage"
+ class: ""
+ state_class: ""
+ uom: "%"
+ scale: 1
+ rule: 1
+ registers: [0x0023]
+ icon: ''
+
+ - name: "ON-OFF Enable"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x002B]
+ isstr: true
+ lookup:
+ - key: 0
+ value: "OFF"
+ - key: 1
+ value: "ON"
+ icon: 'mdi:toggle-switch'
+
+ - name: "Island Protection Enable"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x002E]
+ isstr: true
+ lookup:
+ - key: 0
+ value: "Disabled"
+ - key: 1
+ value: "Enabled"
+ icon: 'mdi:island'
+
+ - name: "Overfrequency&Load-shedding Enable"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0031]
+ isstr: true
+ lookup:
+ - key: 0
+ value: "Disabled"
+ - key: 1
+ value: "Enabled"
+ icon: 'mdi:toggle-switch'
+
+ - group: Inverter
+ items:
+ - name: "Running Status"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x003B]
+ isstr: true
+ lookup:
+ - key: 0
+ value: "Stand-by"
+ - key: 1
+ value: "Self-check"
+ - key: 2
+ value: "Normal"
+ - key: 3
+ value: "Warning"
+ - key: 4
+ value: "Fault"
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Total AC Output Power (Active)"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 0.1
+ rule: 3
+ registers: [0x0056, 0x0057]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Radiator Temperature"
+ class: "temperature"
+ uom: "°C"
+ state_class: "measurement"
+ scale: 0.01
+ rule: 1
+ offset: 1000
+ registers: [0x005a]
+
+ - name: "Inverter ID"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 5
+ registers: [0x0003,0x0004,0x0005,0x0006,0x0007]
+ isstr: true
+
+ - name: "Hardware Version"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 7
+ registers: [0x000C]
+ isstr: true
+
+ - name: "DC Master Firmware Version"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 7
+ registers: [0x000D]
+ isstr: true
+
+ - name: "AC Version. Number"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 7
+ registers: [0x000E]
+ isstr: true
+
+ - name: "Rated Power"
+ class: "energy"
+ state_class: ""
+ uom: "W"
+ scale: 0.1
+ rule: 1
+ registers: [0x0010]
+ icon: 'mdi:solar-power'
+
+ - name: "Communication Protocol Version"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 7
+ registers: [0x0012]
+ isstr: true
+
+ - name: "Start-up Self-checking Time "
+ class: ""
+ state_class: ""
+ uom: "s"
+ scale: 1
+ rule: 1
+ registers: [0x0015]
+ icon: 'mdi:solar-power'
+
+ - name: "Update Time"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 8
+ registers: [0x0016,0x0017,0x0018]
+ isstr: true
+
+ - name: "Soft Start Enable"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x002F]
+ isstr: true
+ lookup:
+ - key: 0
+ value: "Disabled"
+ - key: 1
+ value: "Enabled"
+ icon: 'mdi:toggle-switch'
+
+ - name: "Power Factor Regulation"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 0.1
+ rule: 2
+ registers: [0x0032]
+ icon: ''
+
+ - name: "Restore Factory Settings"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0036]
+ isstr: true
+ lookup:
+ - key: 0
+ value: "Disabled"
+ - key: 1
+ value: "Enabled"
+ icon: 'mdi:factory'
diff --git a/bundles/org.openhab.binding.solarman/src/main/resources/definitions/deye_hybrid.yaml b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/deye_hybrid.yaml
new file mode 100644
index 00000000000..b1e25e5fb85
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/deye_hybrid.yaml
@@ -0,0 +1,548 @@
+requests:
+ - start: 0x0003
+ end: 0x0070
+ mb_functioncode: 0x03
+ - start: 0x0096
+ end: 0x00f8
+ mb_functioncode: 0x03
+
+parameters:
+ - group: solar
+ items:
+ - name: "PV1 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 1
+ registers: [0x00BA]
+ icon: 'mdi:solar-power'
+
+ - name: "PV2 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 1
+ registers: [0x00BB]
+ icon: 'mdi:solar-power'
+
+ - name: "PV1 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x006D]
+ icon: 'mdi:solar-power'
+
+ - name: "PV2 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x006F]
+ icon: 'mdi:solar-power'
+
+ - name: "PV1 Current"
+ class: "current"
+ uom: "A"
+ scale: 0.1
+ rule: 1
+ registers: [0x006E]
+ icon: 'mdi:solar-power'
+
+ - name: "PV2 Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.1
+ rule: 1
+ registers: [0x0070]
+ icon: 'mdi:solar-power'
+
+ - name: "Daily Production"
+ class: "energy"
+ state_class: "measurement"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [0x006C]
+ icon: 'mdi:solar-power'
+
+ - name: "Total Production"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [0x0060,0x0061]
+ icon: 'mdi:solar-power'
+
+ - name: "Micro-inverter Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 1
+ registers: [0x00A6]
+ icon: 'mdi:solar-power'
+
+ - group: Battery
+ items:
+ - name: "Total Battery Charge"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [0x0048,0x0049]
+ icon: 'mdi:battery-plus'
+
+ - name: "Total Battery Discharge"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [0x004A,0x004B]
+ icon: 'mdi:battery-minus'
+
+ - name: "Battery Status"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x00BD]
+ isstr: true
+ lookup:
+ - key: 0
+ value: "Charge"
+ - key: 1
+ value: "Stand-by"
+ - key: 2
+ value: "Discharge"
+ icon: 'mdi:battery'
+
+ - name: "Battery Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [0x00BE]
+ icon: 'mdi:battery'
+
+ - name: "Battery Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.01
+ rule: 1
+ registers: [0x00B7]
+ icon: 'mdi:battery'
+
+ - name: "Battery SOC"
+ class: "battery"
+ state_class: "measurement"
+ uom: "%"
+ scale: 1
+ rule: 1
+ registers: [0x00B8]
+ icon: 'mdi:battery'
+
+ - name: "Battery Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 2
+ registers: [0x00BF]
+ icon: 'mdi:battery'
+
+ - name: "Battery Temperature"
+ class: "temperature"
+ state_class: "measurement"
+ uom: "°C"
+ scale: 0.1
+ rule: 1
+ offset: 1000
+ registers: [0x00B6]
+ icon: 'mdi:battery'
+
+ - group: Grid
+ items:
+ - name: "Total Grid Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [0x00A9]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Grid Voltage L1"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x0096]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Grid Voltage L2"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x0097]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Internal CT L1 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [0x00A7]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Internal CT L2 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [0x00A8]
+ icon: 'mdi:transmission-tower'
+
+ - name: "External CT L1 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [0x00AA]
+ icon: 'mdi:transmission-tower'
+
+ - name: "External CT L2 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [0x00AB]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Daily Energy Bought"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [0x004C]
+ icon: 'mdi:transmission-tower-export'
+
+ - name: "Total Energy Bought"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [0x004E,0x0050]
+ icon: 'mdi:transmission-tower-export'
+
+ - name: "Daily Energy Sold"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [0x004D]
+ icon: 'mdi:transmission-tower-import'
+
+ - name: "Total Energy Sold"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [0x0051,0x0052]
+ icon: 'mdi:transmission-tower-import'
+
+
+ - name: "Total Grid Production"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 4
+ registers: [0x003F,0x0040]
+ icon: 'mdi:transmission-tower'
+
+ - group: Upload
+ items:
+ - name: "Total Load Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 1
+ registers: [0x00B2]
+ icon: 'mdi:lightning-bolt-outline'
+
+ - name: "Load L1 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 1
+ registers: [0x00B0]
+ icon: 'mdi:lightning-bolt-outline'
+
+ - name: "Load L2 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 1
+ registers: [0x00B1]
+ icon: 'mdi:lightning-bolt-outline'
+
+ - name: "Load Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x009D]
+ icon: 'mdi:lightning-bolt-outline'
+
+ - name: "Daily Load Consumption"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [0x0054]
+ icon: 'mdi:lightning-bolt-outline'
+
+ - name: "Total Load Consumption"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [0x0055,0x0056]
+ icon: 'mdi:lightning-bolt-outline'
+
+ - name: "SmartLoad Enable Status"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x00C3]
+ isstr: true
+ lookup:
+ - key: 0
+ value: "OFF"
+ - key: 1
+ value: "ON"
+ icon: 'mdi:lightning-bolt-outline'
+
+ - group: Inverter
+ items:
+ - name: "Running Status"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x003B]
+ isstr: true
+ lookup:
+ - key: 0
+ value: "Stand-by"
+ - key: 1
+ value: "Self-checking"
+ - key: 2
+ value: "Normal"
+ - key: 3
+ value: "FAULT"
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Total Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [0x00AF]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Current L1"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 2
+ registers: [0x00A4]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Current L2"
+ class: "current"
+ uom: "A"
+ scale: 0.01
+ rule: 2
+ registers: [0x00A5]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Inverter L1 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [0x00AD]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Inverter L2 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [0x00AE]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "DC Temperature"
+ class: "temperature"
+ state_class: "measurement"
+ uom: "°C"
+ scale: 0.1
+ rule: 2
+ offset: 1000
+ registers: [0x005A]
+ icon: 'mdi:thermometer'
+
+ - name: "AC Temperature"
+ class: "temperature"
+ state_class: "measurement"
+ uom: "°C"
+ scale: 0.1
+ rule: 2
+ offset: 1000
+ registers: [0x005B]
+ icon: 'mdi:thermometer'
+
+ - name: "Inverter ID"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 5
+ registers: [0x0003,0x0004,0x0005,0x0006,0x0007]
+ isstr: true
+
+ - name: "Communication Board Version No."
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x000E]
+ isstr: true
+
+ - name: "Control Board Version No."
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x000D]
+ isstr: true
+
+ - name: "Grid-connected Status"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x00C2]
+ isstr: true
+ lookup:
+ - key: 0
+ value: "Off-Grid"
+ - key: 1
+ value: "On-Grid"
+
+ - name: "Gen-connected Status"
+ class: ""
+ uom: ""
+ state_class: ""
+ scale: 1
+ rule: 1
+ registers: [0x00A6]
+ isstr: true
+ lookup:
+ - key: 0
+ value: "none"
+ - key: 1
+ value: "On"
+
+ - name: "Gen Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 1
+ registers: [0x00A6]
+
+ - name: "Time of use"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x00F8]
+ isstr: true
+ lookup:
+ - key: 0
+ value: "Disable"
+ - key: 1
+ value: "Enable"
+
+ - name: "Work Mode"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 3
+ registers: [0x00F4,0x00F7]
+ isstr: true
+ lookup:
+ - key: 0
+ value: "Selling First"
+ - key: 1
+ value: "Zero-Export to Load&Solar Sell"
+ - key: 2
+ value: "Zero-Export to Home&Solar Sell"
+ - key: 3
+ value: "Zero-Export to Load"
+ - key: 4
+ value: "Zero-Export to Home"
+ icon: 'mdi:home-lightning-bolt'
+
+ - group: Alert
+ items:
+ - name: "Alert"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 6
+ registers: [0x0065,0x0066,0x0067,0x0068,0x0069,0x006A]
diff --git a/bundles/org.openhab.binding.solarman/src/main/resources/definitions/deye_sg04lp3.yaml b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/deye_sg04lp3.yaml
new file mode 100644
index 00000000000..7d80fe40264
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/deye_sg04lp3.yaml
@@ -0,0 +1,523 @@
+# SUN-8/12K-SG04LP3-EU | 8/12KW | Three Phase | 2 MPPT | Hybrid Inverter | Low Voltage Battery
+# tested with LSW3_15_FFFF_1.0.91R + LSW3_15_FFFF_1.0.84
+
+requests:
+ - start: 0x0003
+ end: 0x0059
+ mb_functioncode: 0x03
+ - start: 0x0202
+ end: 0x022E
+ mb_functioncode: 0x03
+ - start: 0x024A
+ end: 0x024F
+ mb_functioncode: 0x03
+ - start: 0x0256
+ end: 0x027C
+ mb_functioncode: 0x03
+ - start: 0x0284
+ end: 0x028D
+ mb_functioncode: 0x03
+ - start: 0x02A0
+ end: 0x02A7
+ mb_functioncode: 0x03
+
+parameters:
+ - group: solar
+ items:
+ - name: "PV1 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 1
+ registers: [0x02A0]
+ icon: 'mdi:solar-power'
+
+ - name: "PV2 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 1
+ registers: [0x02A1]
+ icon: 'mdi:solar-power'
+
+ - name: "PV1 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x02A4]
+ icon: 'mdi:solar-power'
+
+ - name: "PV2 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x02A6]
+ icon: 'mdi:solar-power'
+
+ - name: "PV1 Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.1
+ rule: 1
+ registers: [0x02A5]
+ icon: 'mdi:solar-power'
+
+ - name: "PV2 Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.1
+ rule: 1
+ registers: [0x02A7]
+ icon: 'mdi:solar-power'
+
+ - name: "Daily Production"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [0x0211]
+ icon: 'mdi:solar-power'
+ validation:
+ max: 100
+ invalidate_all:
+
+ - name: "Total Production"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [0x0216,0x0217]
+ icon: 'mdi:solar-power'
+
+ - group: Battery
+ items:
+
+ - name: "Daily Battery Charge"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [0x0202]
+ icon: 'mdi:battery-plus'
+ - name: "Daily Battery Discharge"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [0x0203]
+ icon: 'mdi:battery-plus'
+
+ - name: "Total Battery Charge"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [0x0204,0x0205]
+ icon: 'mdi:battery-plus'
+
+ - name: "Total Battery Discharge"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [0x0206,0x0207]
+ icon: 'mdi:battery-minus'
+
+ - name: "Battery Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [0x024E]
+ icon: 'mdi:battery'
+
+ - name: "Battery Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.01
+ rule: 1
+ registers: [0x024B]
+ icon: 'mdi:battery'
+
+ - name: "Battery SOC"
+ class: "battery"
+ state_class: "measurement"
+ uom: "%"
+ scale: 1
+ rule: 1
+ registers: [0x024C]
+ icon: 'mdi:battery'
+ validation:
+ min: 0
+ max: 101
+
+ - name: "Battery Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 2
+ registers: [0x024F]
+ icon: 'mdi:battery'
+
+ - name: "Battery Temperature"
+ class: "temperature"
+ state_class: "measurement"
+ uom: "°C"
+ scale: 0.1
+ rule: 1
+ offset: 1000
+ registers: [0x024A]
+ icon: 'mdi:battery'
+ validation:
+ min: 1
+ max: 99
+ invalidate_all:
+
+ - group: Grid
+ items:
+ - name: "Total Grid Power"
+ class: "measurement"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [0x0271]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Grid Voltage L1"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x0256]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Grid Voltage L2"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x0257]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Grid Voltage L3"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x0258]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Internal CT L1 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [0x025C]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Internal CT L2 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [0x025D]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Internal CT L3 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [0x025E]
+ icon: 'mdi:transmission-tower'
+
+ - name: "External CT L1 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [0x0268]
+ icon: 'mdi:transmission-tower'
+
+ - name: "External CT L2 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [0x0269]
+ icon: 'mdi:transmission-tower'
+
+ - name: "External CT L3 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [0x026A]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Daily Energy Bought"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [0x0208]
+ icon: 'mdi:transmission-tower-export'
+
+ - name: "Total Energy Bought"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [0x020A,0x020B]
+ icon: 'mdi:transmission-tower-export'
+
+ - name: "Daily Energy Sold"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [0x0209]
+ icon: 'mdi:transmission-tower-import'
+
+ - name: "Total Energy Sold"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [0x020C,0x020D]
+ icon: 'mdi:transmission-tower-import'
+
+ - name: "Total Grid Production"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 4
+ registers: [0x020C,0x020D]
+ icon: 'mdi:transmission-tower'
+
+ - group: Upload
+ items:
+ - name: "Total Load Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 1
+ registers: [0x028D]
+ icon: 'mdi:lightning-bolt-outline'
+
+ - name: "Load L1 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 1
+ registers: [0x028A]
+ icon: 'mdi:lightning-bolt-outline'
+
+ - name: "Load L2 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 1
+ registers: [0x028B]
+ icon: 'mdi:lightning-bolt-outline'
+
+ - name: "Load L3 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 1
+ registers: [0x028C]
+ icon: 'mdi:lightning-bolt-outline'
+
+ - name: "Load Voltage L1"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x0284]
+ icon: 'mdi:lightning-bolt-outline'
+
+ - name: "Load Voltage L2"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x0285]
+ icon: 'mdi:lightning-bolt-outline'
+
+ - name: "Load Voltage L3"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x0286]
+ icon: 'mdi:lightning-bolt-outline'
+
+ - name: "Daily Load Consumption"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [0x020E]
+ icon: 'mdi:lightning-bolt-outline'
+
+ - name: "Total Load Consumption"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [0x020F,0x0210]
+ icon: 'mdi:lightning-bolt-outline'
+
+ - group: Inverter
+ items:
+ - name: "Current L1"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 2
+ registers: [0x0276]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Current L2"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 2
+ registers: [0x0277]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Current L3"
+ class: "current"
+ uom: "A"
+ scale: 0.01
+ rule: 2
+ registers: [0x0278]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Inverter L1 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [0x0279]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Inverter L2 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [0x027A]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Inverter L3 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [0x027B]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "DC Temperature"
+ class: "temperature"
+ state_class: "measurement"
+ uom: "°C"
+ scale: 0.1
+ rule: 2
+ offset: 1000
+ registers: [0x021C]
+ icon: 'mdi:thermometer'
+
+ - name: "AC Temperature"
+ class: "temperature"
+ state_class: "measurement"
+ uom: "°C"
+ scale: 0.1
+ rule: 2
+ offset: 1000
+ registers: [0x021D]
+ icon: 'mdi:thermometer'
+
+ - name: "Inverter ID"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 5
+ registers: [0x0003,0x0004,0x0005,0x0006,0x0007]
+ isstr: true
+
+ - name: "Communication Board Version No."
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0011]
+ isstr: true
+
+ - name: "Control Board Version No."
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x000D]
+ isstr: true
+
+ - group: Alert
+ items:
+ - name: "Alert"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 6
+ registers: [0x0229,0x022A,0x22B,0x022C,0x022D,0x022E]
diff --git a/bundles/org.openhab.binding.solarman/src/main/resources/definitions/deye_string.yaml b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/deye_string.yaml
new file mode 100644
index 00000000000..566d94f1171
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/deye_string.yaml
@@ -0,0 +1,206 @@
+
+requests:
+ - start: 0x0003
+ end: 0x0070
+ mb_functioncode: 0x03
+
+parameters:
+ - group: solar
+ items:
+ - name: "PV1 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x006D]
+ icon: 'mdi:solar-power'
+
+ - name: "PV2 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x006F]
+ icon: 'mdi:solar-power'
+
+ - name: "PV1 Current"
+ class: "current"
+ uom: "A"
+ scale: 0.1
+ rule: 1
+ registers: [0x006E]
+ icon: 'mdi:solar-power'
+
+ - name: "PV2 Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.1
+ rule: 1
+ registers: [0x0070]
+ icon: 'mdi:solar-power'
+
+ - name: "Daily Production"
+ class: "energy"
+ state_class: "total"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [0x003C]
+ icon: 'mdi:solar-power'
+
+ - name: "Total Production"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [0x003F,0x0040]
+ icon: 'mdi:solar-power'
+ validation:
+ min: 0.1
+ invalidate_all:
+
+
+
+ - group: Grid
+ items:
+ - name: "Grid Voltage L-L(A)"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x0049]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Grid Voltage L-L(B))"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x004A]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Grid Voltage L-L(C)"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x004B]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Grid Current A"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.1
+ rule: 2
+ registers: [0x004C]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Grid Current B"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.1
+ rule: 2
+ registers: [0x004D]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Grid Current C"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.1
+ rule: 2
+ registers: [0x004E]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Grid Frequency"
+ class: "frequency"
+ state_class: "measurement"
+ uom: "Hz"
+ scale: 0.01
+ rule: 1
+ registers: [0x004F]
+ icon: 'mdi:home-lightning-bolt'
+
+ - group: Inverter
+ items:
+ - name: "Running Status"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x003B]
+ isstr: true
+ lookup:
+ - key: 0
+ value: "Stand-by"
+ - key: 1
+ value: "Self-checking"
+ - key: 2
+ value: "Normal"
+ - key: 3
+ value: "FAULT"
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Total Output AC Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 0.1
+ rule: 3
+ registers: [0x0050,0x0051]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Input Active Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 0.1
+ rule: 3
+ registers: [0x0052, 0x0053]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Output Apparent Power"
+ class: "apparent_power"
+ state_class: "measurement"
+ uom: "VA"
+ scale: 0.1
+ rule: 3
+ registers: [0x0054, 0x0055]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Output Active Power"
+ class: "energy"
+ state_class: "measurement"
+ uom: "W"
+ scale: 0.1
+ rule: 3
+ registers: [0x0056, 0x0057]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Output Reactive Power"
+ class: "reactive_power"
+ state_class: "measurement"
+ uom: "VAR"
+ rule: 3
+ scale: 0.1
+ registers: [0x0058, 0x0059]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Inverter ID"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 5
+ registers: [0x0003,0x0004,0x0005,0x0006,0x0007]
+ isstr: true
diff --git a/bundles/org.openhab.binding.solarman/src/main/resources/definitions/kstar_hybrid.yaml b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/kstar_hybrid.yaml
new file mode 100644
index 00000000000..49ec6222955
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/kstar_hybrid.yaml
@@ -0,0 +1,793 @@
+# KSTAR Hybrid Inverter
+# Modbus information taken from "MODBUS RS485 Communication Protocol V2.5" document provided by KSTAR
+
+#INPUT_REGISTERS = 3000 - 3660 # 0x0BB8 - 0x0E4C
+#HOLDING_REGISTERS = 3200 - 3237 # 0x0C80 - 0x0C9B
+
+requests:
+ # Input registers 3000 - 3667
+ - start: 3000
+ end: 3125
+ mb_functioncode: 0x04
+
+ # Input registers 3200 - 3228 not read as they would clash with holding registers
+ - start: 3125
+ end: 3200
+ mb_functioncode: 0x04
+
+ - start: 3228
+ end: 3250
+ mb_functioncode: 0x04
+
+ - start: 3250
+ end: 3375
+ mb_functioncode: 0x04
+
+ - start: 3375
+ end: 3500
+ mb_functioncode: 0x04
+
+ # Holding registers 3200 - 3237. Inverter system information.
+ - start: 3200
+ end: 3218
+ mb_functioncode: 0x03
+
+parameters:
+ - group: solar
+ items:
+ - name: "PV1 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [ 3000 ]
+ icon: 'mdi:solar-power'
+
+ - name: "PV2 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [ 3001 ]
+ icon: 'mdi:solar-power'
+
+ - name: "PV1 Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 2
+ registers: [ 3012 ]
+ icon: 'mdi:solar-power'
+
+ - name: "PV2 Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 2
+ registers: [ 3013 ]
+ icon: 'mdi:solar-power'
+
+ - name: "PV1 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [ 3024 ]
+ icon: 'mdi:solar-power'
+
+ - name: "PV2 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [ 3025 ]
+ icon: 'mdi:solar-power'
+
+ - name: "Daily Production"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [ 3036 ]
+ icon: 'mdi:solar-power'
+
+ - name: "Monthly Production"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 1
+ rule: 3
+ registers: [ 3038, 3037 ]
+ icon: 'mdi:solar-power'
+
+ - name: "Yearly Production"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 1
+ rule: 3
+ registers: [ 3040, 3039 ]
+ icon: 'mdi:solar-power'
+
+ - name: "Cumulative Production"
+ class: "energy"
+ state_class: "total"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [ 3042, 3041 ]
+ icon: 'mdi:solar-power'
+
+ - group: Power Grid
+ items:
+ # Should this be the sum of the 3 phases "Meter Power"?
+ - name: "Total Grid Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [ 3100 ]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Daily Energy Purchased"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [ 3109 ]
+ icon: 'mdi:transmission-tower-import'
+
+ - name: "Monthly Energy Purchased"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 1
+ rule: 3
+ registers: [ 3111, 3110 ]
+ icon: 'mdi:transmission-tower-import'
+
+ - name: "Yearly Energy Purchased"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 1
+ rule: 3
+ registers: [ 3113, 3112 ]
+ icon: 'mdi:transmission-tower-import'
+
+ - name: "Cumulative Energy Purchased"
+ class: "energy"
+ state_class: "total"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [ 3115, 3114 ]
+ icon: 'mdi:transmission-tower-import'
+
+ - name: "Daily Energy Feed-In"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [ 3116 ]
+ icon: 'mdi:transmission-tower-export'
+
+ - name: "Monthly Energy Feed-In"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 1
+ rule: 3
+ registers: [ 3118, 3117 ]
+ icon: 'mdi:transmission-tower-export'
+
+ - name: "Yearly Energy Feed-In"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 1
+ rule: 3
+ registers: [ 3120, 3119 ]
+ icon: 'mdi:transmission-tower-export'
+
+ - name: "Cumulative Grid Feed-In"
+ class: "energy"
+ state_class: "total"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [ 3122, 3121 ]
+ icon: 'mdi:transmission-tower-export'
+
+ - group: Electricity Consumption
+ items:
+ # Should this be the sum of the 3 phases "Load Power"?
+ - name: "Total Consumption Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 1
+ registers: [ 3144 ]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Daily Consumption"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [ 3147 ]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Monthly Consumption"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 1
+ rule: 3
+ registers: [ 3149, 3148 ]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Yearly Consumption"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 1
+ rule: 3
+ registers: [ 3151, 3150 ]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Cumulative Consumption"
+ class: "energy"
+ state_class: "total"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [ 3153, 3152 ]
+ icon: 'mdi:home-lightning-bolt'
+
+ - group: Battery
+ items:
+ - name: "Battery Type"
+ class: "battery"
+ state_class: "measurement"
+ uom: ''
+ scale: 1
+ rule: 1
+ registers: [ 3062 ]
+ icon: 'mdi:battery'
+ lookup:
+ - key: 1
+ value: "Lead-Acid"
+ - key: 6
+ value: "LFP"
+
+ - name: "Battery Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.01
+ rule: 1
+ registers: [ 3063 ]
+ icon: 'mdi:battery-charging'
+
+ - name: "Battery Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.1
+ rule: 2
+ registers: [ 3064 ]
+ icon: 'mdi:battery-charging-10'
+
+ - name: "Battery Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [ 3065 ]
+ icon: 'mdi:battery-charging-high'
+
+ - name: "Battery SoC"
+ class: "battery"
+ state_class: "measurement"
+ uom: "%"
+ scale: 0.1
+ rule: 1
+ registers: [ 3066 ]
+ icon: 'mdi:battery'
+
+ - name: "Battery Temperature"
+ class: "temperature"
+ state_class: "measurement"
+ uom: "°C"
+ scale: 0.1
+ rule: 2
+ registers: [ 3067 ]
+ icon: 'mdi:battery-heart-outline'
+
+ - name: "Battery Discharge Capacity Depth"
+ class: "battery"
+ state_class: "measurement"
+ uom: "%"
+ scale: 1
+ rule: 1
+ registers: [ 3068 ]
+ icon: 'mdi:battery-20'
+ validation:
+ min: 10
+ max: 95
+
+ - name: "Battery Radiator Temperature"
+ class: "temperature"
+ state_class: "measurement"
+ uom: "°C"
+ scale: 0.1
+ rule: 2
+ registers: [ 3056 ]
+ icon: 'mdi:battery-heart-outline'
+
+ - name: "Battery Total Discharge"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 1
+ rule: 3
+ registers: [ 3293, 3292 ]
+ icon: 'mdi:battery-minus-variant'
+
+ - name: "Battery Daily Discharge"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [ 3294 ]
+ icon: 'mdi:battery-minus-variant'
+
+ - name: "Battery Total Charge"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 1
+ rule: 3
+ registers: [ 3300, 3299 ]
+ icon: 'mdi:battery-minus-variant'
+
+ - name: "Battery Daily Charge"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [ 3301 ]
+ icon: 'mdi:battery-plus-variant'
+
+ - group: Inverter Information
+ items:
+ - name: "Inverter Working Mode"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [3044]
+ lookup:
+ - key: 0
+ value: "Self Consumption"
+ - key: 1
+ value: "Peak Shift"
+ - key: 2
+ value: "Battery Priority"
+ icon: 'mdi:wrench'
+
+ - name: "Inverter Model"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [3045]
+ lookup:
+ # Single-phase models
+ - key: 0
+ value: "KSE-2K-048S"
+ - key: 1
+ value: "KSE-3K-048S"
+ - key: 2
+ value: "KSE-3.6K-048S"
+ - key: 3
+ value: "KSE-4.6K-048S"
+ - key: 4
+ value: "KSE-5K-048S"
+ - key: 5
+ value: "KSE-3.6K-048"
+ - key: 6
+ value: "KSE-4.6K-048"
+ - key: 7
+ value: "KSE-5K-048"
+ - key: 8
+ value: "KSE-6K-048"
+ - key: 9
+ value: "BluE-S 3680D"
+ - key: 11
+ value: "BluE-S 5000D"
+ - key: 12
+ value: "BluE-S 6000D"
+ - key: 14
+ value: "KSE-3K-048S M1"
+ - key: 15
+ value: "BluE-S 3680D M1"
+ - key: 17
+ value: "BluE-S 5000D M1"
+ - key: 18
+ value: "BluE-S 6000D M1"
+ # Three-phase models
+ - key: 32
+ value: "E10KT"
+ - key: 33
+ value: "E8KT"
+ - key: 34
+ value: "E12KT"
+ icon: 'mdi:wrench'
+
+ - name: "System status"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [3046]
+ lookup:
+ - key: 0
+ value: "Initialize"
+ - key: 1
+ value: "Stand-by"
+ - key: 2
+ value: "Hybrid Grid"
+ - key: 3
+ value: "Off-Network"
+ - key: 4
+ value: "Mains Charging"
+ - key: 5
+ value: "PV Charging"
+ - key: 6
+ value: "Mains Bypass"
+ - key: 7
+ value: "Fault"
+ - key: 8
+ value: "Debug"
+ - key: 9
+ value: "Forced Charge"
+ - key: 10
+ value: "Power on the device separately from the"
+ - key: 11
+ value: "DSP Burn"
+ - key: 12
+ value: "MCU Burn"
+ - key: 13
+ value: "Permanent Error"
+ icon: 'mdi:wrench'
+
+ - name: "Inverter status"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [3047]
+ lookup:
+ - key: 0
+ value: "Stand-by"
+ - key: 1
+ value: "Off-Grid"
+ - key: 2
+ value: "On-Grid"
+ - key: 3
+ value: "Off-Grid to On-Grid"
+ - key: 4
+ value: "On-Grid to Off-Grid"
+ icon: 'mdi:wrench'
+
+ - name: "DCDC status"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [3048]
+ lookup:
+ - key: 0
+ value: "Stand-by"
+ - key: 1
+ value: "Soft Boot"
+ - key: 2
+ value: "Charging Mode"
+ - key: 3
+ value: "Discharging Mode"
+ icon: 'mdi:wrench'
+
+ - name: "DSP Alarm Code"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 6
+ registers: [3050, 3049]
+ icon: 'mdi:wrench'
+
+ - name: "DSP Error Code"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 6
+ registers: [3052, 3051]
+ icon: 'mdi:wrench'
+
+ - name: "Grid Standard"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [3193]
+ lookup:
+ - key: 0
+ value: "China"
+ - key: 1
+ value: "Germany"
+ - key: 2
+ value: "Australia"
+ - key: 3
+ value: "Italy"
+ - key: 4
+ value: "Spain"
+ - key: 5
+ value: "UK"
+ - key: 6
+ value: "Hungary"
+ - key: 7
+ value: "Belgium"
+ - key: 8
+ value: "West Australia"
+ - key: 9
+ value: "Greece"
+ - key: 10
+ value: "France"
+ - key: 11
+ value: "Bangkok"
+ - key: 12
+ value: "Thailand"
+ - key: 13
+ value: "South Africa"
+ - key: 14
+ value: "EN50549"
+ - key: 15
+ value: "Brazil"
+ - key: 16
+ value: "VDE0126"
+ - key: 17
+ value: "Ireland"
+ - key: 18
+ value: "Israel"
+ - key: 19
+ value: "Poland"
+ - key: 20
+ value: "Chile"
+ - key: 21
+ value: "Local"
+ icon: 'mdi:wrench'
+
+ - name: "Inverter Model Name"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 5
+ registers: [3200, 3201, 3202, 3203, 3204, 3205, 3206, 3207]
+ icon: 'mdi:wrench'
+
+ - name: "Inverter Battery Name"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 5
+ registers: [3208, 3209, 3210, 3211, 3212, 3213, 3214, 3215]
+ icon: 'mdi:wrench'
+
+ # ARM AND DSP version numbers ("VX.Y.Z") are set in the two bytes on each register. The first byte contains the
+ # X.Y part (scale 0.1), and the second by contains the Z part. How should we transform these values from a number
+ # to a parsed string?
+ - name: "ARM Version Number"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [3216]
+ icon: 'mdi:wrench'
+
+ - name: "DSP Version Number"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [3217]
+ icon: 'mdi:wrench'
+
+ - name: "Inverter SN Number"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 5
+ registers: [3228, 3229, 3230, 3231, 3232, 3233, 3234, 3235, 3236, 3237, 3238]
+ icon: 'mdi:wrench'
+
+ - group: Inverter
+ items:
+ - name: "Inverter Bus Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [ 3053 ]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Inverter DC Bus Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [ 3054 ]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Inverter Radiator Temperature"
+ class: "temperature"
+ state_class: "measurement"
+ uom: "°C"
+ scale: 0.1
+ rule: 2
+ registers: [ 3055 ]
+ icon: 'mdi:thermometer'
+
+ - name: "Chassis Internal Temperature"
+ class: "temperature"
+ state_class: "measurement"
+ uom: "°C"
+ scale: 0.1
+ rule: 2
+ registers: [ 3057 ]
+ icon: 'mdi:battery-heart-outline'
+
+ # Different phases for 3-phase inverters. Only some models have 3 phases, see "Inverter Model" item
+ # - R: Referent
+ # - S: Secondary
+ # - T: Tertiary
+ - group: R Phase
+ items:
+ - name: "R-phase Grid Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [ 3097 ]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "R-phase Grid Frequency"
+ class: "frequency"
+ state_class: "measurement"
+ uom: "Hz"
+ scale: 0.01
+ rule: 1
+ registers: [ 3098 ]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "R-phase Meter Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.001
+ rule: 2
+ registers: [ 3099 ]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "R-phase Grid Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [ 3100 ]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "R-phase Inverter Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [ 3123 ]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "R-phase Inverter Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 2
+ registers: [ 3124 ]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "R-phase Inverter Frequency"
+ class: "frequency"
+ state_class: "measurement"
+ uom: "Hz"
+ scale: 0.01
+ rule: 1
+ registers: [ 3125 ]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "R-phase Inverter Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 2
+ registers: [ 3126 ]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "R-phase Backup Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [ 3135 ]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "R-phase Backup Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [ 3136 ]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "R-phase Backup Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 1
+ registers: [ 3137 ]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "R-phase Load Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 1
+ registers: [ 3144 ]
+ icon: 'mdi:home-lightning-bolt'
+
diff --git a/bundles/org.openhab.binding.solarman/src/main/resources/definitions/sofar_g3hyd.yaml b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/sofar_g3hyd.yaml
new file mode 100644
index 00000000000..ba30139fef2
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/sofar_g3hyd.yaml
@@ -0,0 +1,1407 @@
+# Sofar G3 also HYD 5-20KTL-3PH
+# This works also for rebranded ZCS Azzurro 3-Phase inverters such as the 3PH HYD6000 ZSS
+# Note that this won't work if your ZCS inverter is connected via Connext, you have to be using a Wi-Fi or Ethernet Kit such as ZSM-WIFI-USB.
+requests:
+ - start: 0x0404
+ end: 0x0420
+ mb_functioncode: 0x03
+ - start: 0x0484
+ end: 0x04AF
+ mb_functioncode: 0x03
+# off - grid info
+# - start: 0x0504
+# end: 0x051F
+# mb_functioncode: 0x03
+ - start: 0x0584
+ end: 0x0589
+ mb_functioncode: 0x03
+ - start: 0x0604
+ end: 0x060A # end of first battery after this continue battery pack 2,3,4
+ mb_functioncode: 0x03
+ - start: 0x0684
+ end: 0x069B
+ mb_functioncode: 0x03
+parameters:
+
+ - group: Inverter
+ items:
+ - name: "Inverter status"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [ 0x0404 ]
+ isstr: true
+ lookup:
+ - key: 0
+ value: "waiting"
+ - key: 1
+ value: "detection"
+ - key: 2
+ value: "grid-connected"
+ - key: 3
+ value: "emergency power supply"
+ - key: 4
+ value: "recoverable fault"
+ - key: 5
+ value: "permanent fault"
+ - key: 6
+ value: "upgrade"
+ - key: 7
+ value: "self-charging"
+ icon: 'mdi:wrench'
+ - name: "Ambient temperature 1"
+ class: "temperature"
+ uom: "°C"
+ scale: 1
+ rule: 2
+ registers: [ 0x0418 ]
+ icon: 'mdi:thermometer'
+ - name: "Ambient temperature 2"
+ class: "temperature"
+ uom: "°C"
+ scale: 1
+ rule: 2
+ registers: [ 0x0419 ]
+ icon: 'mdi:thermometer'
+ - name: "Radiator temperature 1"
+ class: "temperature"
+ uom: "°C"
+ scale: 1
+ rule: 2
+ registers: [ 0x041A ]
+ icon: 'mdi:thermometer'
+ - name: "Radiator temperature 2"
+ class: "temperature"
+ uom: "°C"
+ scale: 1
+ rule: 2
+ registers: [ 0x041B ]
+ icon: 'mdi:thermometer'
+ - name: "Radiator temperature 3"
+ class: "temperature"
+ uom: "°C"
+ scale: 1
+ rule: 2
+ registers: [ 0x041C ]
+ icon: 'mdi:thermometer'
+ - name: "Radiator temperature 4"
+ class: "temperature"
+ uom: "°C"
+ scale: 1
+ rule: 2
+ registers: [ 0x041D ]
+ icon: 'mdi:thermometer'
+ - name: "Radiator temperature 5"
+ class: "temperature"
+ uom: "°C"
+ scale: 1
+ rule: 2
+ registers: [ 0x041E ]
+ icon: 'mdi:thermometer'
+ - name: "Radiator temperature 6"
+ class: "temperature"
+ uom: "°C"
+ scale: 1
+ rule: 2
+ registers: [ 0x041F ]
+ icon: 'mdi:thermometer'
+ - name: "Module temperature 1"
+ class: "temperature"
+ uom: "°C"
+ scale: 1
+ rule: 2
+ registers: [ 0x0420 ]
+ icon: 'mdi:thermometer'
+ - name: "Module temperature 2"
+ class: "temperature"
+ uom: "°C"
+ scale: 1
+ rule: 2
+ registers: [ 0x0421 ]
+ icon: 'mdi:thermometer'
+ - name: "Module temperature 3"
+ class: "temperature"
+ uom: "°C"
+ scale: 1
+ rule: 2
+ registers: [ 0x0422 ]
+ icon: 'mdi:thermometer'
+
+ - group: InverterDC
+ items:
+ - name: "PV1 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [ 0x0584 ]
+ icon: 'mdi:solar-power'
+ - name: "PV1 Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [ 0x0585 ]
+ icon: 'mdi:solar-power'
+ - name: "PV1 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 1
+ registers: [ 0x0586 ]
+ icon: 'mdi:solar-power'
+ - name: "PV2 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [ 0x0587 ]
+ icon: 'mdi:solar-power'
+ - name: "PV2 Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [ 0x0588 ]
+ icon: 'mdi:solar-power'
+ - name: "PV2 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 1
+ registers: [ 0x0589 ]
+ icon: 'mdi:solar-power'
+
+ - group: Battery
+ items:
+ - name: "Battery 1 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [ 0x0604 ]
+ icon: 'mdi:battery'
+ - name: "Battery 1 Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 2
+ registers: [ 0x0605 ]
+ icon: 'mdi:current-dc'
+ - name: "Battery 1 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x0606 ]
+ icon: 'mdi:battery-charging'
+ - name: "Battery 1 Temperature"
+ class: "temperature"
+ state_class: "measurement"
+ uom: "°C"
+ scale: 1
+ rule: 2
+ registers: [ 0x0607 ]
+ icon: 'mdi:battery'
+ - name: "Battery 1 SOC"
+ class: "battery"
+ state_class: "measurement"
+ uom: "%"
+ scale: 1
+ rule: 1
+ registers: [ 0x0608 ]
+ icon: 'mdi:battery'
+ - name: "Battery 1 SOH"
+ class: "battery"
+ state_class: "measurement"
+ uom: "%"
+ scale: 1
+ rule: 1
+ registers: [ 0x0609 ]
+ icon: 'mdi:battery'
+ - name: "Battery 1 Number of Cycles"
+ class: ""
+ state_class: "measurement"
+ uom: "cycle"
+ scale: 1
+ rule: 1
+ registers: [ 0x060A ]
+ icon: 'mdi:battery'
+ - name: "Battery 2 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [ 0x060B ]
+ icon: 'mdi:battery'
+ - name: "Battery 2 Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 2
+ registers: [ 0x060C ]
+ icon: 'mdi:current-dc'
+ - name: "Battery 2 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x060D ]
+ icon: 'mdi:battery-charging'
+ - name: "Battery 2 Temperature"
+ class: "temperature"
+ state_class: "measurement"
+ uom: "°C"
+ scale: 1
+ rule: 2
+ registers: [ 0x060E ]
+ icon: 'mdi:battery'
+ - name: "Battery 2 SOC"
+ class: "battery"
+ state_class: "measurement"
+ uom: "%"
+ scale: 1
+ rule: 1
+ registers: [ 0x060F ]
+ icon: 'mdi:battery'
+ - name: "Battery 2 SOH"
+ class: "battery"
+ state_class: "measurement"
+ uom: "%"
+ scale: 1
+ rule: 1
+ registers: [ 0x0610 ]
+ icon: 'mdi:battery'
+ - name: "Battery 2 Number of Cycles"
+ class: ""
+ state_class: "measurement"
+ uom: "cycle"
+ scale: 1
+ rule: 1
+ registers: [ 0x0611 ]
+ icon: 'mdi:battery'
+
+ - group: GridAC
+ items:
+ - name: "Grid Frequency"
+ class: "current"
+ state_class: "measurement"
+ uom: "Hz"
+ scale: 0.01
+ rule: 1
+ registers: [ 0x0484 ]
+ icon: 'mdi:home-lightning-bolt'
+ - name: "ActivePower_Output_Total"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x0485 ]
+ icon: 'mdi:home-lightning-bolt'
+ - name: "ReactivePower_Output_Total"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x0486 ]
+ icon: 'mdi:home-lightning-bolt'
+ - name: "ApparentPower_Output_Total"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x0487 ]
+ icon: 'mdi:home-lightning-bolt'
+ - name: "ActivePower_PCC_Total"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x0488 ]
+ icon: 'mdi:home-lightning-bolt'
+ - name: "ReactivePower_PCC_Total"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x0489 ]
+ icon: 'mdi:home-lightning-bolt'
+ - name: "ApparentPower_PCC_Total"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x048A ]
+ icon: 'mdi:home-lightning-bolt'
+ - name: "Voltage_Phase_R"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [ 0x048D ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "Current_Output_R"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [ 0x048E ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "ActivePower_Output_R"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x048F ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "ReactivePower_Output_R"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x0490 ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "PowerFactor_Output_R"
+ class: "powerfactor"
+ state_class: "measurement"
+ uom: "p.u."
+ scale: 0.001
+ rule: 2
+ registers: [ 0x0491 ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "Current_PCC_R"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [ 0x0492 ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "ActivePower_PCC_R"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x0493 ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "ReactivePower_PCC_R"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x0494 ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "PowerFactor_PCC_R"
+ class: "powerfactor"
+ state_class: "measurement"
+ uom: "p.u."
+ scale: 0.001
+ rule: 2
+ registers: [ 0x0495 ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "Voltage_Phase_S"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [ 0x0498 ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "Current_Output_S"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [ 0x0499 ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "ActivePower_Output_S"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x049A ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "ReactivePower_Output_S"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x049B ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "PowerFactor_Output_S"
+ class: "powerfactor"
+ state_class: "measurement"
+ uom: "p.u."
+ scale: 0.001
+ rule: 2
+ registers: [ 0x049C ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "Current_PCC_S"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [ 0x049D ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "ActivePower_PCC_S"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x049E ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "ReactivePower_PCC_S"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x049F ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "PowerFactor_PCC_S"
+ class: "powerfactor"
+ state_class: "measurement"
+ uom: "p.u."
+ scale: 0.001
+ rule: 2
+ registers: [ 0x04A0 ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "Voltage_Phase_T"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [ 0x04A3 ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "Current_Output_T"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [ 0x04A4 ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "ActivePower_Output_T"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x04A5 ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "ReactivePower_Output_T"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x04A6 ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "PowerFactor_Output_T"
+ class: "powerfactor"
+ state_class: "measurement"
+ uom: "p.u."
+ scale: 0.001
+ rule: 2
+ registers: [ 0x04A7 ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "Current_PCC_T"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [ 0x04A8 ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "ActivePower_PCC_T"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x04A9 ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "ReactivePower_PCC_T"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x04AA ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "PowerFactor_PCC_T"
+ class: "powerfactor"
+ state_class: "measurement"
+ uom: "p.u."
+ scale: 0.001
+ rule: 2
+ registers: [ 0x04AB ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "ActivePower_PV_Ext"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 1
+ registers: [ 0x04AE ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "ActivePower_Load_Sys"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x04AF ]
+ icon: 'mdi:lightning-bolt-outline'
+
+ - group: GridEPS
+ items:
+ - name: "ActivePower_Load_Total_EPS"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x0504 ]
+ icon: 'mdi:home-lightning-bolt'
+ - name: "ReactivePower_Load_Total_EPS"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x0505 ]
+ icon: 'mdi:home-lightning-bolt'
+ - name: "ApparentPower_Load_Total_EPS"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x0506 ]
+ icon: 'mdi:home-lightning-bolt'
+ - name: "Frequency_Output_EPS"
+ class: "current"
+ state_class: "measurement"
+ uom: "Hz"
+ scale: 0.01
+ rule: 1
+ registers: [ 0x0507 ]
+ icon: 'mdi:home-lightning-bolt'
+ - name: "Voltage_Output_R_EPS"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [ 0x050A ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "Current_Load_R_EPS"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [ 0x050B ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "ActivePower_Load_R_EPS"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x050C ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "ReactivePower_Load_R_EPS"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x050D ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "ApparentPower_Load_R_EPS"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x050E ]
+ icon: 'mdi:home-lightning-bolt'
+ - name: "LoadPeakRatio_R_EPS"
+ class: "powerfactor"
+ state_class: "measurement"
+ uom: "p.u."
+ scale: 0.01
+ rule: 2
+ registers: [ 0x050F ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "Voltage_Output_S_EPS"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [ 0x0512 ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "Current_Load_S_EPS"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [ 0x0513 ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "ActivePower_Load_S_EPS"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x0514 ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "ReactivePower_Load_S_EPS"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x0515 ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "ApparentPower_Load_S_EPS"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x0516 ]
+ icon: 'mdi:home-lightning-bolt'
+ - name: "LoadPeakRatio_S_EPS"
+ class: "powerfactor"
+ state_class: "measurement"
+ uom: "p.u."
+ scale: 0.01
+ rule: 2
+ registers: [ 0x0517 ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "Voltage_Output_T_EPS"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [ 0x051A ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "Current_Load_T_EPS"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [ 0x051B ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "ActivePower_Load_T_EPS"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x051C ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "ReactivePower_Load_T_EPS"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x051D ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "ApparentPower_Load_T_EPS"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 2
+ registers: [ 0x051E ]
+ icon: 'mdi:home-lightning-bolt'
+ - name: "LoadPeakRatio_T_EPS"
+ class: "powerfactor"
+ state_class: "measurement"
+ uom: "p.u."
+ scale: 0.01
+ rule: 2
+ registers: [ 0x051F ]
+ icon: 'mdi:lightning-bolt-outline'
+
+ - group: Generation
+ items:
+ - name: "Daily PV Generation"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.01
+ rule: 3
+ registers: [ 0x0685,0x0684 ]
+ icon: 'mdi:solar-power'
+ - name: "Total PV Generation"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [ 0x0687,0x0686 ]
+ icon: 'mdi:solar-power'
+ - name: "Daily Load Consumption"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.01
+ rule: 3
+ registers: [ 0x0689,0x0688 ]
+ icon: 'mdi:lightning-bolt-outline'
+ - name: "Total Load Consumption"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [ 0x068B,0x068A ]
+ icon: 'mdi:solar-power'
+ - name: "Daily Energy Bought"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.01
+ rule: 3
+ registers: [ 0x068D,0x068C ]
+ icon: 'mdi:transmission-tower-export'
+ - name: "Total Energy Bought"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [ 0x068F,0x068E ]
+ icon: 'mdi:transmission-tower-export'
+ - name: "Daily Energy Sold"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.01
+ rule: 3
+ registers: [ 0x0691,0x0690 ]
+ icon: 'mdi:transmission-tower-import'
+ - name: "Total Energy Sold"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [ 0x0693,0x0692 ]
+ icon: 'mdi:transmission-tower-import'
+ - name: "Daily Battery Charge"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.01
+ rule: 3
+ registers: [ 0x0695,0x0694 ]
+ icon: 'mdi:battery-plus'
+ - name: "Total Battery Charge"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [ 0x0697,0x0696 ]
+ icon: 'mdi:battery-plus'
+ - name: "Daily Battery Discharge"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.01
+ rule: 3
+ registers: [ 0x0699,0x0698 ]
+ icon: 'mdi:battery-minus'
+ - name: "Total Battery Discharge"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 3
+ registers: [ 0x069b,0x069A ]
+ icon: 'mdi:battery-minus'
+
+ - group: Alert
+ items:
+ - name: "Alert"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 6
+ registers: [ 0x0405,0x0406,0x0407,0x0408,0x0409,0x040A,0x040B,0x040C,0x040D,0x040E,0x040F,0x0410 ]
+
+ - name: "Fault 1"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [ 0x0405 ]
+ isstr: true
+ icon: 'mdi:wrench'
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: "ID01 Grid Over Voltage Protection"
+ - key: 2
+ value: "ID02 Grid Under Voltage Protection"
+ - key: 4
+ value: "ID03 Grid Over Frequency Protection"
+ - key: 8
+ value: "ID04 Grid Under Frequency Protection"
+ - key: 16
+ value: "ID05 Leakage current fault"
+ - key: 32
+ value: "ID06 High penetration error"
+ - key: 64
+ value: "ID07 Low penetration error"
+ - key: 128
+ value: "ID08 Islanding error"
+ - key: 256
+ value: "ID09 Grid voltage transient value overvoltage 1"
+ - key: 512
+ value: "ID10 Grid voltage transient value overvoltage 2"
+ - key: 1024
+ value: "ID11 Grid line voltage error"
+ - key: 2048
+ value: "ID12 Inverter voltage error"
+ - key: 4096
+ value: "ID13 Anti-backflow overload"
+ - key: 8192
+ value: "ID14"
+ - key: 16384
+ value: "ID15"
+ - key: 32768
+ value: "ID16"
+ - name: "Fault 2"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ icon: 'mdi:wrench'
+ isstr: true
+ registers: [ 0x0406 ]
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: "ID17 Grid current sampling error"
+ - key: 2
+ value: "ID18 Grid current DC component sampling error (AC side)"
+ - key: 4
+ value: "ID19 Grid voltage sampling error (DC side)"
+ - key: 8
+ value: "ID20 Grid voltage sampling error (AC side)"
+ - key: 16
+ value: "ID21 Leakage current sampling error (DC side)"
+ - key: 32
+ value: "ID22 Leakage current sampling error (AC side)"
+ - key: 64
+ value: "ID23 Load voltage DC component sampling error"
+ - key: 128
+ value: "ID24 DC input current sampling error"
+ - key: 256
+ value: "ID25 DC component sampling error of grid current (DC side)"
+ - key: 512
+ value: "ID26 DC input branch current sampling error"
+ - key: 1024
+ value: "ID27"
+ - key: 2048
+ value: "ID28"
+ - key: 4096
+ value: "ID29 Leakage current consistency error"
+ - key: 8192
+ value: "ID30 Grid voltage consistency error"
+ - key: 16384
+ value: "ID31 DCI consistency error"
+ - key: 32768
+ value: "ID32"
+ - name: "Fault 3"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ icon: 'mdi:wrench'
+ isstr: true
+
+ registers: [ 0x0407 ]
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: 'ID033 SPI communication error (DC side)'
+ - key: 2
+ value: 'ID034 SPI communication error (AC side)'
+ - key: 4
+ value: 'ID035 Chip error (DC side)'
+ - key: 8
+ value: 'ID036 Chip error (AC side)'
+ - key: 16
+ value: 'ID037 Auxiliary power error'
+ - key: 32
+ value: 'ID038 Inverter soft start failure'
+ - key: 64
+ value: 'ID039 '
+ - key: 128
+ value: 'ID040 '
+ - key: 256
+ value: 'ID041 Relay detection failure'
+ - key: 512
+ value: 'ID042 Low insulation impedance'
+ - key: 1024
+ value: 'ID043 Grounding error'
+ - key: 2048
+ value: 'ID044 Input mode setting error'
+ - key: 4096
+ value: 'ID045 CT error'
+ - key: 8192
+ value: 'ID046 Input reversal error'
+ - key: 16384
+ value: 'ID047 Parallel error'
+ - key: 32768
+ value: 'ID048 Serial number error'
+ - name: "Fault 4"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ icon: 'mdi:wrench'
+ registers: [ 0x0408 ]
+ isstr: true
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: 'ID049 Battery temperature protection'
+ - key: 2
+ value: 'ID050 Heat sink 1 temperature protection'
+ - key: 4
+ value: 'ID051 Heater 2 temperature protection'
+ - key: 8
+ value: 'ID052 Heater 3 temperature protection'
+ - key: 16
+ value: 'ID053 Heatsink 4 temperature protection'
+ - key: 32
+ value: 'ID054 Heatsink 5 temperature protection'
+ - key: 64
+ value: 'ID055 Radiator 6 temperature protection'
+ - key: 128
+ value: 'ID056 '
+ - key: 256
+ value: 'ID057 Ambient temperature 1 protection'
+ - key: 512
+ value: 'ID058 Ambient temperature 2 protection'
+ - key: 1024
+ value: 'ID059 Module 1 temperature protection'
+ - key: 2048
+ value: 'ID060 Module 2 temperature protection'
+ - key: 4096
+ value: 'ID061 Module 3 temperature protection'
+ - key: 8192
+ value: 'ID062 Module temperature difference is too large'
+ - key: 16384
+ value: 'ID063 '
+ - key: 32768
+ value: 'ID064 '
+ - name: "Fault 5"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ icon: 'mdi:wrench'
+ registers: [ 0x0409 ]
+ isstr: true
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: 'ID065 Bus voltage RMS unbalance'
+ - key: 2
+ value: 'ID066 Bus voltage transient value unbalance'
+ - key: 4
+ value: 'ID067 Undervoltage of busbar during grid connection'
+ - key: 8
+ value: 'ID068 Bus bar low voltage'
+ - key: 16
+ value: 'ID069 PV overvoltage'
+ - key: 32
+ value: 'ID070 Battery over-voltage'
+ - key: 64
+ value: 'ID071 LLCBus overvoltage protection'
+ - key: 128
+ value: 'ID072 Inverter bus voltage RMS software overvoltage'
+ - key: 256
+ value: 'ID073 Inverter bus voltage transient value software overvoltage'
+ - key: 512
+ value: 'ID074 Flying Cross Capacitor Overvoltage Protection'
+ - key: 1024
+ value: 'ID075 Flying Cross capacitor undervoltage protection'
+ - key: 2048
+ value: 'ID076 '
+ - key: 4096
+ value: 'ID077 '
+ - key: 8192
+ value: 'ID078 '
+ - key: 16384
+ value: 'ID079 '
+ - key: 32768
+ value: 'ID080 '
+ - name: "Fault 6"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ icon: 'mdi:wrench'
+ isstr: true
+ registers: [ 0x040A ]
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: 'ID081 Battery overcurrent software protection'
+ - key: 2
+ value: 'ID082 Dci overcurrent protection'
+ - key: 4
+ value: 'ID083 Output transient current protection'
+ - key: 8
+ value: 'ID084 BuckBoost software overcurrent'
+ - key: 16
+ value: 'ID085 Output RMS current protection'
+ - key: 32
+ value: 'ID086 PV instantaneous current overcurrent software protection'
+ - key: 64
+ value: 'ID087 PV parallel uneven current'
+ - key: 128
+ value: 'ID088 Output current unbalance'
+ - key: 256
+ value: 'ID089 PV software overcurrent protection'
+ - key: 512
+ value: 'ID090 Balanced circuit overcurrent protection'
+ - key: 1024
+ value: 'ID091 Resonance protection'
+ - key: 2048
+ value: 'ID092 '
+ - key: 4096
+ value: 'ID093 '
+ - key: 8192
+ value: 'ID094 '
+ - key: 16384
+ value: 'ID095 '
+ - key: 32768
+ value: 'ID096 '
+ - name: "Fault 7"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ icon: 'mdi:wrench'
+ isstr: true
+ registers: [ 0x040B ]
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: 'ID097 LLC bus hardware overvoltage'
+ - key: 2
+ value: 'ID098 Inverter bus hardware overvoltage'
+ - key: 4
+ value: 'ID099 BuckBoost hardware overcurrent'
+ - key: 8
+ value: 'ID100 Battery hardware overcurrent'
+ - key: 16
+ value: 'ID101 '
+ - key: 32
+ value: 'ID102 PV hardware overcurrent'
+ - key: 64
+ value: 'ID103 AC output hardware overcurrent'
+ - key: 128
+ value: 'ID104 '
+ - key: 256
+ value: 'ID105 Power meter error'
+ - key: 512
+ value: 'ID106 Serial number model error'
+ - key: 1024
+ value: 'ID107 '
+ - key: 2048
+ value: 'ID108 '
+ - key: 4096
+ value: 'ID109 '
+ - key: 8192
+ value: 'ID110 Overload protection 1'
+ - key: 16384
+ value: 'ID111 Overload protection 2'
+ - key: 32768
+ value: 'ID112 Overload protection 3'
+
+ - name: "Fault 8"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ icon: 'mdi:wrench'
+ registers: [ 0x040C ]
+ isstr: true
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: 'ID113 Overtemperature derating'
+ - key: 2
+ value: 'ID114 Frequency down load'
+ - key: 4
+ value: 'ID115 Frequency loading'
+ - key: 8
+ value: 'ID116 Voltage down load'
+ - key: 16
+ value: 'ID117 Voltage loading'
+ - key: 32
+ value: 'ID118 '
+ - key: 64
+ value: 'ID119 '
+ - key: 128
+ value: 'ID120 '
+ - key: 256
+ value: 'ID121 Lightning protection failure (DC)'
+ - key: 512
+ value: 'ID122 Lightning protection failure (AC)'
+ - key: 1024
+ value: 'ID123 '
+ - key: 2048
+ value: 'ID124 Battery low voltage protection'
+ - key: 4096
+ value: 'ID125 Battery low voltage shutdown'
+ - key: 8192
+ value: 'ID126 Battery low voltage pre-alarm'
+ - key: 16384
+ value: 'ID127 '
+ - key: 32768
+ value: 'ID128 '
+ - name: "Fault 9"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ icon: 'mdi:wrench'
+ isstr: true
+
+ registers: [ 0x040D ]
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: 'ID129 Output hardware overcurrent permanent fault'
+ - key: 2
+ value: 'ID130 Bus overvoltage permanent fault'
+ - key: 4
+ value: 'ID131 Bus hardware over-voltage permanent fault'
+ - key: 8
+ value: 'ID132 PV uneven flow permanent fault'
+ - key: 16
+ value: 'ID133 Battery overcurrent permanent fault in EPS mode'
+ - key: 32
+ value: 'ID134 Output transient overcurrent permanent fault'
+ - key: 64
+ value: 'ID135 Output current unbalance permanent fault'
+ - key: 128
+ value: 'ID136 '
+ - key: 256
+ value: 'ID137 Input mode setting error permanent fault'
+ - key: 512
+ value: 'ID138 Input overcurrent permanent fault'
+ - key: 1024
+ value: 'ID139 Input hardware overcurrent permanent fault'
+ - key: 2048
+ value: 'ID140 Relay permanent fault'
+ - key: 4096
+ value: 'ID141 Bus unbalance permanent fault'
+ - key: 8192
+ value: 'ID142 Lightning protection permanent fault - DC side'
+ - key: 16384
+ value: 'ID143 Lightning protection permanent fault - AC side'
+ - key: 32768
+ value: 'ID144 '
+ - name: "Fault 10"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ icon: 'mdi:wrench'
+ isstr: true
+ registers: [ 0x040E ]
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: 'ID145 USB fault'
+ - key: 2
+ value: 'ID146 WIFI fault'
+ - key: 4
+ value: 'ID147 Bluetooth fault'
+ - key: 8
+ value: 'ID148 RTC clock fault'
+ - key: 16
+ value: 'ID149 Communication board EEPROM error'
+ - key: 32
+ value: 'ID150 Communication board FLASH error'
+ - key: 64
+ value: 'ID151 '
+ - key: 128
+ value: 'ID152 Safety regulation version error'
+ - key: 256
+ value: 'ID153 SCI communication error (DC side)'
+ - key: 512
+ value: 'ID154 SCI communication error (AC side)'
+ - key: 1024
+ value: 'ID155 SCI communication error (convergence board side)'
+ - key: 2048
+ value: 'ID156 Software version inconsistency'
+ - key: 4096
+ value: 'ID157 Lithium battery 1 communication error'
+ - key: 8192
+ value: 'ID158 Li-ion battery 2 communication error'
+ - key: 16384
+ value: 'ID159 Lithium battery 3 communication error'
+ - key: 32768
+ value: 'ID160 Lithium battery 4 communication failure'
+ - name: "Fault 11"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ icon: 'mdi:wrench'
+ registers: [ 0x040F ]
+ isstr: true
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: 'ID161 Forced shutdown'
+ - key: 2
+ value: 'ID162 Remote shutdown'
+ - key: 4
+ value: 'ID163 Drms0 shutdown'
+ - key: 8
+ value: 'ID164 '
+ - key: 16
+ value: 'ID165 Remote down load'
+ - key: 32
+ value: 'ID166 Logic interface down load'
+ - key: 64
+ value: 'ID167 Anti-Reverse Flow Downgrade'
+ - key: 128
+ value: 'ID168 '
+ - key: 256
+ value: 'ID169 Fan 1 failure'
+ - key: 512
+ value: 'ID170 Fan 2 failure'
+ - key: 1024
+ value: 'ID171 Fan 3 failure'
+ - key: 2048
+ value: 'ID172 Fan 4 failure'
+ - key: 4096
+ value: 'ID173 Fan 5 failure'
+ - key: 8192
+ value: 'ID174 Fan 6 failure'
+ - key: 16384
+ value: 'ID175 Fan 7 fault'
+ - key: 32768
+ value: 'ID176 Meter communication failure'
+ - name: "Fault 12"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ icon: 'mdi:wrench'
+ registers: [ 0x0410 ]
+ isstr: true
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: 'ID177 BMS over-voltage alarm'
+ - key: 2
+ value: 'ID178 BMS undervoltage alarm'
+ - key: 4
+ value: 'ID179 BMS high temperature alarm'
+ - key: 8
+ value: 'ID180 BMS low temperature alarm'
+ - key: 16
+ value: 'ID181 BMS charge/discharge overload alarm'
+ - key: 32
+ value: 'ID182 BMS short circuit alarm'
+ - key: 64
+ value: 'ID183 BMS version inconsistency'
+ - key: 128
+ value: 'ID184 BMS CAN version inconsistency'
+ - key: 256
+ value: 'ID185 BMS CAN version is too low'
+ - key: 512
+ value: 'ID186 '
+ - key: 1024
+ value: 'ID187 '
+ - key: 2048
+ value: 'ID188 '
+ - key: 4096
+ value: 'ID189 Arc device communication failure'
+ - key: 8192
+ value: 'ID190 DC arc alarm fault'
+ - key: 16384
+ value: 'ID191 PID repair failed'
+ - key: 32768
+ value: 'ID192 PLC module heartbeat loss'
\ No newline at end of file
diff --git a/bundles/org.openhab.binding.solarman/src/main/resources/definitions/sofar_hyd3k-6k-es.yaml b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/sofar_hyd3k-6k-es.yaml
new file mode 100644
index 00000000000..1b379dfcaac
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/sofar_hyd3k-6k-es.yaml
@@ -0,0 +1,1147 @@
+# Configuration file for Sofar HYD3000/4000/5000/6000-ES
+# inverter family.
+
+requests:
+ - start: 0x0200
+ end: 0x0255
+ mb_functioncode: 0x03
+ - start: 0x10B0
+ end: 0x10BC
+ mb_functioncode: 0x04
+
+
+parameters:
+ - group: solar
+ items:
+ - name: "PV Instant Generated PW"
+ class: "energy"
+ state_class: "measurement"
+ uom: "kW"
+ scale: 0.01
+ rule: 1
+ registers: [0x0215]
+ icon: 'mdi:solar-power'
+
+
+ - name: "PV1 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "kW"
+ scale: 0.01
+ rule: 1
+ registers: [0x0252]
+ icon: 'mdi:solar-power'
+
+ - name: "PV2 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "kW"
+ scale: 0.01
+ rule: 1
+ registers: [0x0255]
+ icon: 'mdi:solar-power'
+
+ - name: "PV1 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x0250]
+ icon: 'mdi:solar-power'
+
+ - name: "PV2 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x0253]
+ icon: 'mdi:solar-power'
+
+ - name: "PV1 Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [0x0251]
+ icon: 'mdi:solar-power'
+
+ - name: "PV2 Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [0x0254]
+ icon: 'mdi:solar-power'
+
+ - name: "Daily Production"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.01
+ rule: 1
+ registers: [0x0218]
+ icon: 'mdi:solar-power'
+
+ - name: "Total Production"
+ class: "energy"
+ state_class: "total"
+ uom: "kWh"
+ scale: 1
+ rule: 3
+ registers: [0x021D,0x021C]
+ icon: 'mdi:solar-power'
+
+ - name: "Total generation time"
+ class: ""
+ state_class: "measurement"
+ uom: "h"
+ scale: 1
+ rule: 3
+ registers: [0x0245,0x0244]
+ icon: 'mdi:clock-outline'
+
+ - name: "Today generation time"
+ class: ""
+ state_class: "total_increasing"
+ uom: "min"
+ scale: 1
+ rule: 1
+ registers: [0x0243]
+ icon: 'mdi:clock-outline'
+
+ - name: "Today Grid Return"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.01
+ rule: 1
+ registers: [0x0219]
+ icon: 'mdi:transmission-tower-export'
+
+ - name: "Today Grid Consumption"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.01
+ rule: 1
+ registers: [0x021A]
+ icon: 'mdi:transmission-tower-import'
+
+ - name: "Today Power Consumption"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.01
+ rule: 1
+ registers: [0x021B]
+ icon: 'mdi:lightning-bolt'
+
+ - name: "Total Grid Return"
+ class: "energy"
+ state_class: "total"
+ uom: "KWh"
+ scale: 1
+ rule: 3
+ registers: [0x021F,0x021E]
+ icon: 'mdi:transmission-tower-export'
+
+ - name: "Total Grid Consumption"
+ class: "Energy"
+ state_class: "total"
+ uom: "KWh"
+ scale: 1
+ rule: 3
+ registers: [0x0221,0x0220]
+ icon: 'mdi:transmission-tower-import'
+
+ - name: "Total Power Consumption"
+ class: "energy"
+ state_class: "total"
+ uom: "KWh"
+ scale: 1
+ rule: 3
+ registers: [0x0223,0x0222]
+ icon: 'mdi:lightning-bolt'
+
+ - group: Output
+ items:
+
+ - name: "Power Consumption"
+ class: ""
+ state_class: ""
+ uom: "KW"
+ scale: 0.01
+ rule: 1
+ registers: [0x0213]
+ icon: ''
+
+# - name: "Output active power"
+# class: "power"
+# state_class: "measurement"
+# uom: "W"
+# scale: 10
+# rule: 1
+# registers: [0x000C]
+# icon: 'mdi:home-lightning-bolt'
+
+# - name: "Output reactive power"
+# class: ""
+# state_class: "measurement"
+# uom: "kVar"
+# scale: 0.01
+# rule: 1
+# registers: [0x000D]
+# icon: 'mdi:home-lightning-bolt'
+
+ - name: "Grid frequency"
+ class: "frequency"
+ state_class: "measurement"
+ uom: "Hz"
+ scale: 0.01
+ rule: 1
+ registers: [0x020C]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Grid Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x0206]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Grid Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 2
+ registers: [0x0207]
+ icon: 'mdi:home-lightning-bolt'
+
+
+ - group: batteries
+ items:
+ - name: "Battery Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x020E]
+ icon: 'mdi:battery-charging'
+
+ - name: "Battery Charge / Discharge current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 2
+ registers: [0x020F]
+ icon: 'mdi:battery-charging-10'
+
+ - name: "Battery Percentage"
+ class: "battery"
+ state_class: "measurement"
+ uom: "%"
+ scale: 1
+ rule: 1
+ registers: [0x0210]
+ icon: 'mdi:battery'
+
+ - name: "Battery Temperature"
+ class: "temperature"
+ state_class: "measurement"
+ uom: "°C"
+ scale: 1
+ #se non funziona cambia questo in 2
+ rule: 1
+ registers: [0x0211]
+ icon: 'mdi:battery-heart-outline'
+
+ - name: "Battery Daily Charge"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.01
+ rule: 1
+ registers: [0x0224]
+ icon: 'mdi:battery-clock'
+
+ - name: "Battery Total Energy Charged"
+ class: "energy"
+ state_class: "total"
+ uom: "kWh"
+ scale: 1
+ rule: 1
+ registers: [0x0227]
+ icon: 'mdi:battery-clock'
+
+ - name: "Battery Total Energy Dischaged"
+ class: "energy"
+ state_class: "total"
+ uom: "kWh"
+ scale: 1
+ rule: 1
+ registers: [0x0229]
+ icon: 'mdi:battery-clock-outline'
+
+ - name: "Battery Cicles"
+ class: ""
+ state_class: ""
+ uom: "Charges"
+ scale: 1
+ rule: 1
+ registers: [0x022C]
+ icon: 'mdi:battery-check-outline'
+
+ - name: "Battery Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "KW"
+ scale: 0.01
+ rule: 2
+ registers: [0x0237]
+ icon: 'mdi:battery-charging-high'
+
+ - name: "Battery Type"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x10B0]
+ icon: 'mdi:battery'
+
+ - name: "Battery Capacity"
+ class: ""
+ state_class: ""
+ uom: "Ah"
+ scale: 1
+ rule: 1
+ registers: [0x10B1]
+ icon: 'mdi:battery'
+
+ - name: "Battery daily Discharge"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.01
+ rule: 1
+ registers: [0x0225]
+ icon: 'mdi:battery'
+
+ - name: "Battery Total Charge"
+ class: "energy"
+ state_class: "total"
+ uom: "KWh"
+ scale: 1
+ rule: 3
+ registers: [0x0227,0x0226]
+ icon: 'mdi:battery'
+
+ - name: "Battery Total Discharge"
+ class: "energy"
+ state_class: "total"
+ uom: "KWh"
+ scale: 1
+ rule: 3
+ registers: [0x0229,0x0228]
+ icon: 'mdi:battery'
+
+ - name: "Max Charge Voltage"
+ class: ""
+ state_class: ""
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x10B3]
+ icon: 'mdi:battery'
+
+ - name: "Max Charge Current"
+ class: ""
+ state_class: ""
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [0x10B4]
+ icon: 'mdi:battery'
+
+ - name: "Over Voltage Protection"
+ class: ""
+ state_class: ""
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x10B5]
+ icon: 'mdi:battery'
+
+ - name: "Min Discharge Voltage"
+ class: ""
+ state_class: ""
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x10B6]
+ icon: 'mdi:battery'
+
+ - name: "Max Discharge Current"
+ class: ""
+ state_class: ""
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [0x10B7]
+ icon: 'mdi:battery'
+
+ - name: "Undervoltage Protection"
+ class: ""
+ state_class: ""
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x10B8]
+ icon: 'mdi:battery'
+
+ - name: "Discharge Depth"
+ class: ""
+ state_class: ""
+ uom: "%"
+ scale: 1
+ rule: 1
+ registers: [0x10B9]
+ icon: 'mdi:battery'
+
+ - name: "Periods Of Discharge Time"
+ class: ""
+ state_class: ""
+ uom: "h"
+ scale: 1
+ rule: 1
+ registers: [0x10BA]
+ icon: 'mdi:battery'
+
+ - name: "Empty Battery Voltage"
+ class: ""
+ state_class: ""
+ uom: "V"
+ scale: 0.01
+ rule: 1
+ registers: [0x10BB]
+ icon: 'mdi:battery'
+
+ - name: "Full Battery Voltage"
+ class: ""
+ state_class: ""
+ uom: "V"
+ scale: 0.01
+ rule: 1
+ registers: [0x10BC]
+ icon: 'mdi:battery'
+
+ - group: Inverter
+ items:
+ - name: "Inverter status"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0200]
+ lookup:
+ - key: 0
+ value: "Stand-by"
+ - key: 1
+ value: "Self-Checking"
+ - key: 2
+ value: "Normal"
+ - key: 3
+ value: "Discharging Check State"
+ - key: 4
+ value: "Discharging State"
+ - key: 5
+ value: "EPS State"
+ - key: 6
+ value: "Fault State"
+ - key: 7
+ value: "Permanent State"
+ icon: 'mdi:state-machine'
+
+ - name: "Inverter module temperature"
+ class: "temperature"
+ uom: "°C"
+ scale: 1
+ rule: 2
+ registers: [0x0239]
+ icon: 'mdi:thermometer'
+
+ - name: "Inverter inner temperature"
+ class: "temperature"
+ state_class: "measurement"
+ uom: "°C"
+ scale: 1
+ rule: 2
+ registers: [0x0238]
+ icon: 'mdi:thermometer'
+
+ - name: "Inverter bus voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 2
+ registers: [0x022D]
+ icon: 'mdi:home-lightning-bolt'
+
+# - name: "PV1 voltage sample by slave CPU"
+# class: "voltage"
+# state_class: "measurement"
+# uom: "V"
+# scale: 0.1
+# rule: 1
+# registers: [0x001E]
+# icon: 'mdi:home-lightning-bolt'
+
+# - name: "PV1 current sample by slave CPU"
+# class: "current"
+# state_class: "measurement"
+# uom: "A"
+# scale: 0.1
+# rule: 1
+# registers: [0x001F]
+# icon: 'mdi:home-lightning-bolt'
+
+ - name: "Countdown time"
+ class: ""
+ state_class: "measurement"
+ uom: "s"
+ scale: 1
+ rule: 1
+ registers: [0x022A]
+ icon: ''
+
+# - name: "Input mode"
+# class: ""
+# state_class: ""
+# uom: ""
+# scale: 1
+# rule: 1
+# registers: [0x0022]
+# icon: ''
+
+ - name: "Communication Board inner message"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0242]
+ icon: ''
+
+ - name: "Insulation of PV1+ to ground"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0246]
+ icon: ''
+
+ - name: "Insulation of PV2+ to ground"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0247]
+ icon: ''
+
+ - name: "Insulation of PV- to ground"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0248]
+ icon: ''
+
+ - name: "Country"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x023A]
+ lookup:
+ - key: 0
+ value: "Germany"
+ - key: 1
+ value: "CEI0-21 Internal"
+ - key: 2
+ value: "Australia"
+ - key: 3
+ value: "Spain RD1699"
+ - key: 4
+ value: "Turkey"
+ - key: 5
+ value: "Denmark"
+ - key: 6
+ value: "Greece"
+ - key: 7
+ value: "Netherland"
+ - key: 8
+ value: "Belgium"
+ - key: 9
+ value: "UK-G59"
+ - key: 10
+ value: "China"
+ - key: 11
+ value: "France"
+ - key: 12
+ value: "Poland"
+ - key: 13
+ value: "Germany BDEW"
+ - key: 14
+ value: "Germany VDE0126"
+ - key: 15
+ value: "Italy CEI0-16"
+ - key: 16
+ value: "UK-G83"
+ - key: 17
+ value: "Greece Islands"
+ - key: 18
+ value: "EU EN50438"
+ - key: 19
+ value: "EU EN61727"
+ - key: 20
+ value: "Korea"
+ - key: 21
+ value: "Sweden"
+ - key: 22
+ value: "Europe General"
+ - key: 23
+ value: "CEI0-21 External"
+ - key: 24
+ value: "Cyprus"
+ - key: 25
+ value: "India"
+ - key: 26
+ value: "Philippines"
+ - key: 27
+ value: "New Zeland"
+ - key: 28
+ value: "Reserve"
+ - key: 29
+ value: "Reserve"
+ icon: ''
+
+ - group: Alert
+ items:
+ - name: "Inverter alert message"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x022B]
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: "ID01 The power grid voltage is too high"
+ - key: 2
+ value: "ID02 The power grid voltage is too low"
+ - key: 3
+ value: "ID03 The power grid frequency is too high"
+ - key: 4
+ value: "ID04 The power grid frequency is too low"
+ - key: 5
+ value: "ID05 The battery voltage is too high"
+ - key: 7
+ value: "ID07 GridLVRT fault"
+ - key: 8
+ value: "ID08 The PV voltage is too high"
+ - key: 9
+ value: "ID09 LLCBus voltage is too high and has triggered hardware protection"
+ - key: 10
+ value: "ID10 Boost voltage is too high and has triggered hardware protection"
+ - key: 11
+ value: "ID11 BuckBoost current is too high and has triggered hardware protection"
+ - key: 12
+ value: "ID12 The battery current is too high and has triggered hardware protection"
+ - key: 13
+ value: "ID13 The GFCI sampling value between the master DSP and slave DSP is not consistent"
+ - key: 14
+ value: "ID14 The PV current is too high and has triggered hardware protection"
+ - key: 15
+ value: "ID15 The grid current is too high and has triggered hardware protection"
+ - key: 16
+ value: "ID16 Input current is not balanced"
+ - key: 17
+ value: "ID17 Grid current sampling error"
+ - key: 18
+ value: "ID18 DCI sampling error"
+ - key: 19
+ value: "ID19 Grid voltage sampling error"
+ - key: 20
+ value: "ID20 GFCI sampling error"
+ - key: 21
+ value: "ID21 Master chip fault"
+ - key: 22
+ value: "ID22 Auxiliary voltage error"
+ - key: 25
+ value: "ID25 LLCBus voltage is too high"
+ - key: 26
+ value: "ID26 Bus voltage is too high and has triggered software protection"
+ - key: 27
+ value: "ID27 Battery current is too high"
+ - key: 28
+ value: "ID28 The DCI is too high"
+ - key: 29
+ value: "ID29 The grid current is too high"
+ - key: 30
+ value: "ID30 Bunk current is too high"
+ - key: 31
+ value: "ID31 The output current is too high"
+ - key: 32
+ value: "ID32 The input current is too high"
+ - key: 33
+ value: "ID33 Incorrect input mode"
+ - key: 48
+ value: "ID48 The GFCI sampling value between the master DSP and slave DSP is not consistent"
+ - key: 49
+ value: "ID49 The grid voltage sampling value between the master DSP and slave DSP is no consistent"
+ - key: 50
+ value: "ID50 The grid frequency sampling value between the master DSP and slave DSP is no consistent"
+ - key: 51
+ value: "ID51 The DCI sampling value between the master DSP and slave DSP is no consistent"
+ - key: 52
+ value: "ID52 HYD-ES inverter can't communicate with Lithium battery BMS correctly"
+ - key: 53
+ value: "ID53 SPI communication fault"
+ - key: 54
+ value: "ID54 SCI communication fault"
+ - key: 55
+ value: "ID55 Relays fault"
+ - key: 56
+ value: "ID56 Insulation resistance is too low"
+ - key: 57
+ value: "ID57 Battery temperature is too high"
+ - key: 58
+ value: "ID58 Heat sink temperature is too high"
+ - key: 59
+ value: "ID59 Environment temperature is too high"
+ - key: 60
+ value: "ID60 PE connectFault"
+ - key: 65
+ value: "ID65 The grid current is too high and has caused unrecoverable hardware fault"
+ - key: 66
+ value: "ID66 The bus voltage is too high and has caused unrecoverable fault"
+ - key: 67
+ value: "ID67 Unrecoverable fault of battery overcurrent in EPS mode"
+ - key: 68
+ value: "ID68 The input current is unbalanced and has triggered an unrecoverable fault"
+ - key: 70
+ value: "ID70 The grid current is too high and has triggered an unrecoverable fault"
+ - key: 73
+ value: "ID73 The input current is too high and has triggered an unrecoverable fault"
+ - key: 74
+ value: "ID74 Incorrect input mode"
+ - key: 75
+ value: "ID75 Unrecoverable EEPROM write"
+ - key: 76
+ value: "ID76 Unrecoverable EEPROM read"
+ - key: 77
+ value: "ID77 Relay has triggered permanent fault"
+ - key: 81
+ value: "ID81 Internal temperature is too high"
+ - key: 82
+ value: "ID82 AC frequency is too high"
+ - key: 83
+ value: "ID83 Remote power derate"
+ - key: 84
+ value: "ID84 Switch OFF HYD series inverter remotely"
+ - key: 85
+ value: "ID85 SOC <= 1 - DOD or Low battery voltage"
+ - key: 86
+ value: "ID86 Battery voltage is too low and caused HYD series inverter to switch OFF"
+ - key: 94
+ value: "ID94 Software version is not consistent"
+ - key: 95
+ value: "ID95 The communication board EEPROM is faulty"
+ - key: 96
+ value: "ID96 RTC clock chip fault"
+ - key: 98
+ value: "ID98 SD card fault"
+ - key: 100
+ value: "ID100 Battery overcurrent discharging protection"
+ - key: 101
+ value: "ID101 Discharging short circuit protection"
+ - key: 102
+ value: "ID102 Battery high voltage protection"
+ - key: 103
+ value: "ID103 Battery low voltage protection"
+ - key: 104
+ value: "ID104 Battery high temperature protection while discharging"
+ - key: 105
+ value: "ID105 Battery high temperature protection while charging"
+ - key: 106
+ value: "ID106 Battery low temperature protection while discharging"
+ - key: 107
+ value: "ID107 Battery low temperature protection while charging"
+ icon: 'mdi:alert'
+
+ - name: "Fault 1"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0201]
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: "ID01 Grid Over Voltage Protection"
+ - key: 2
+ value: "ID02 Grid Under Voltage Protection"
+ - key: 4
+ value: "ID03 Grid Over Frequency Protection"
+ - key: 8
+ value: "ID04 Grid Under Frequency Protection"
+ - key: 16
+ value: "ID05 Battery Over Voltage"
+ - key: 32
+ value: "ID06"
+ - key: 64
+ value: "ID07"
+ - key: 128
+ value: "ID08"
+ - key: 256
+ value: "ID09 LLCBus Over Voltage Hardware"
+ - key: 512
+ value: "ID10 Bus Over Voltage Hardware"
+ - key: 1024
+ value: "ID11 BuckBoost over Current Hardware"
+ - key: 2048
+ value: "ID12 Battery over Current Hardware"
+ - key: 4096
+ value: "ID13"
+ - key: 8192
+ value: "ID14"
+ - key: 16384
+ value: "ID15 Output Current Hardware"
+ - key: 32768
+ value: "ID16"
+ icon: 'mdi:wrench'
+
+
+ - name: "Fault 2"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0202]
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: "ID17 Grid current sampling error"
+ - key: 2
+ value: "ID18 DCI sampling error"
+ - key: 4
+ value: "ID19 Grid voltage sampling error"
+ - key: 8
+ value: "ID20"
+ - key: 16
+ value: "ID21 Main chip fault"
+ - key: 32
+ value: "ID22 Hardware auxiliary power fault"
+ - key: 64
+ value: "ID23"
+ - key: 128
+ value: "ID24"
+ - key: 256
+ value: "ID25 LLCBus Over Current protection"
+ - key: 512
+ value: "ID26 Bus over voltage protection"
+ - key: 1024
+ value: "ID27 Battery Over Current protection"
+ - key: 2048
+ value: "ID28 Dci Over Current Protection"
+ - key: 4096
+ value: "ID29 Output over current software"
+ - key: 8192
+ value: "ID30 Buck Over Current"
+ - key: 16384
+ value: "ID31 Output over current protection"
+ - key: 32768
+ value: "ID32 The input current is too high"
+ icon: 'mdi:wrench'
+
+ - name: "Fault 3"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0203]
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: "ID33 Reserved"
+ - key: 2
+ value: "ID34 Reserved"
+ - key: 4
+ value: "ID35 Reserved"
+ - key: 8
+ value: "ID36 Reserved"
+ - key: 16
+ value: "ID37 Reserved"
+ - key: 32
+ value: "ID38 Reserved"
+ - key: 64
+ value: "ID39 Reserved"
+ - key: 128
+ value: "ID40 Reserved"
+ - key: 256
+ value: "ID41 Reserved"
+ - key: 512
+ value: "ID42 Reserved"
+ - key: 1024
+ value: "ID43 Reserved"
+ - key: 2048
+ value: "ID44 Reserved"
+ - key: 4096
+ value: "ID45 Reserved"
+ - key: 8192
+ value: "ID46 Reserved"
+ - key: 16384
+ value: "ID47 Reserved"
+ - key: 32768
+ value: "ID48 Reserved"
+ icon: 'mdi:wrench'
+
+ - name: "Fault 4"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0204]
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: "ID49 Grid voltage sampling value between master and slave DSP vary widely"
+ - key: 2
+ value: "ID50 Grid frequency sampling value between master and slave DSP vary widely"
+ - key: 4
+ value: "ID51 DCI sampling value between master and slave DSP vary widely"
+ - key: 8
+ value: "ID52 GFCI sampling value between master and slave DSP vary widely"
+ - key: 16
+ value: "ID53 Communication failure between master and slave DSP failure"
+ - key: 32
+ value: "ID53 Communication failure between slave and communication board"
+ - key: 64
+ value: "ID55 Relay fault"
+ - key: 128
+ value: "ID56"
+ - key: 256
+ value: "ID57 Inverter temp is too high"
+ - key: 512
+ value: "ID58 Boost temp is too high"
+ - key: 1024
+ value: "ID59 Environment temp is too high"
+ - key: 2048
+ value: "ID60"
+ - key: 4096
+ value: "ID61 Reserved"
+ - key: 8192
+ value: "ID62 Reserved"
+ - key: 16384
+ value: "ID63 Reserved"
+ - key: 32768
+ value: "ID64 Reserved"
+ icon: 'mdi:wrench'
+
+ - name: "Fault 5"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0205]
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: "ID65 Grid current is too high and causes unrecoverable fault"
+ - key: 2
+ value: "ID66 Bus voltage is too high and causes unrecoverable fault"
+ - key: 4
+ value: "ID67 EPS Mode Battery Over current,and has cause unrecoverable fault"
+ - key: 8
+ value: "ID68"
+ - key: 16
+ value: "ID69"
+ - key: 32
+ value: "ID70 The Output current is too high,and has cause unrecoverable fault"
+ - key: 64
+ value: "ID71"
+ - key: 128
+ value: "ID72 Reserved"
+ - key: 256
+ value: "ID73 Reserved"
+ - key: 512
+ value: "ID74"
+ - key: 1024
+ value: "ID75 Error writing from EEPROM"
+ - key: 2048
+ value: "ID76 Error reading to EEPROM"
+ - key: 4096
+ value: "ID77 Relay fauilure causes unrecoverable fault"
+ - key: 8192
+ value: "ID78 Reserved"
+ - key: 16384
+ value: "ID79 Reserved"
+ - key: 32768
+ value: "ID80 Reserved"
+ icon: 'mdi:wrench'
+
+ - group: Other energy
+ items:
+ - name: "Buck Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [0x022F]
+ icon: 'mdi:lightning-bolt'
+
+ - name: "Grid R Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x0230]
+ icon: 'mdi:lightning-bolt'
+
+ - name: "Grid R Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [0x0231]
+ icon: 'mdi:lightning-bolt'
+
+ - name: "Generation Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [0x0236]
+ icon: 'mdi:lightning-bolt'
+
+
+ - name: "Charge / Discharge Power"
+ class: ""
+ state_class: ""
+ uom: "KW"
+ scale: 0.01
+ rule: 2
+ registers: [0x020D]
+ icon: ''
+
+ - name: "Feed in / out power"
+ class: ""
+ state_class: ""
+ uom: "KW"
+ scale: 0.01
+ rule: 2
+ registers: [0x0212]
+ icon: ''
+
+ - name: "Input/Output Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "KW"
+ scale: 0.01
+ rule: 2
+ registers: [0x0214]
+ icon: 'mdi:lightning-bolt'
+
+ - name: "Energy Management Model"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x10B2]
+ icon: ''
+
+ - name: "DCI Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "mA"
+ scale: 1
+ rule: 1
+ registers: [0x023B]
+ icon: 'mdi:lightning-bolt'
+
+ - name: "DCI Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x023C]
+ icon: 'mdi:lightning-bolt'
+
+ - name: "Grid S Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x0232]
+ icon: 'mdi:lightning-bolt'
+
+ - name: "Grid S Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [0x0233]
+ icon: 'mdi:lightning-bolt'
+
+ - name: "Grid T voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x0234]
+ icon: 'mdi:lightning-bolt'
+
+ - name: "Grid T current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [0x0235]
+ icon: 'mdi:lightning-bolt'
\ No newline at end of file
diff --git a/bundles/org.openhab.binding.solarman/src/main/resources/definitions/sofar_lsw3.yaml b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/sofar_lsw3.yaml
new file mode 100644
index 00000000000..7edc3eb168a
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/sofar_lsw3.yaml
@@ -0,0 +1,602 @@
+requests:
+ - start: 0x0000
+ end: 0x0027
+ mb_functioncode: 0x03
+
+
+parameters:
+ - group: solar
+ items:
+ - name: "PV1 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 1
+ registers: [0x000A]
+ icon: 'mdi:solar-power'
+
+ - name: "PV2 Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 1
+ registers: [0x000B]
+ icon: 'mdi:solar-power'
+
+ - name: "PV1 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x0006]
+ icon: 'mdi:solar-power'
+
+ - name: "PV2 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x0008]
+ icon: 'mdi:solar-power'
+
+ - name: "PV1 Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [0x0007]
+ icon: 'mdi:solar-power'
+
+ - name: "PV2 Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [0x0009]
+ icon: 'mdi:solar-power'
+
+ - name: "Daily Production"
+ class: "energy"
+ state_class: "total"
+ uom: "kWh"
+ scale: 0.01
+ rule: 1
+ registers: [0x0019]
+ icon: 'mdi:solar-power'
+
+ - name: "Total Production"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 1
+ rule: 3
+ registers: [0x0016,0x0015]
+ icon: 'mdi:solar-power'
+
+ - name: "Total generation time"
+ class: ""
+ state_class: "measurement"
+ uom: "h"
+ scale: 1
+ rule: 3
+ registers: [0x0018,0x0017]
+ icon: 'mdi:clock-outline'
+
+ - name: "Today generation time"
+ class: ""
+ state_class: "measurement"
+ uom: "min"
+ scale: 1
+ rule: 1
+ registers: [0x001A]
+ icon: 'mdi:clock-outline'
+
+ - group: Output
+ items:
+ - name: "Output active power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 10
+ rule: 1
+ registers: [0x000C]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Output reactive power"
+ class: ""
+ state_class: "measurement"
+ uom: "kVar"
+ scale: 0.01
+ rule: 1
+ registers: [0x000D]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Grid frequency"
+ class: "frequency"
+ state_class: "measurement"
+ uom: "Hz"
+ scale: 0.01
+ rule: 1
+ registers: [0x000E]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "L1 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x000F]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "L1 Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [0x0010]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "L2 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x0011]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "L2 Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [0x0012]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "L3 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x0013]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "L3 Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [0x0014]
+ icon: 'mdi:home-lightning-bolt'
+
+ - group: Inverter
+ items:
+ - name: "Inverter status"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0000]
+ lookup:
+ - key: 0
+ value: "Stand-by"
+ - key: 1
+ value: "Self-checking"
+ - key: 2
+ value: "Normal"
+ - key: 3
+ value: "FAULT"
+ - key: 4
+ value: "Permanent"
+ icon: 'mdi:wrench'
+
+ - name: "Inverter module temperature"
+ class: "temperature"
+ uom: "°C"
+ scale: 1
+ rule: 1
+ registers: [0x001B]
+ icon: 'mdi:thermometer'
+
+ - name: "Inverter inner temperature"
+ class: "temperature"
+ state_class: "measurement"
+ uom: "°C"
+ scale: 1
+ rule: 1
+ registers: [0x001C]
+ icon: 'mdi:thermometer'
+
+ - name: "Inverter bus voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x001D]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "PV1 voltage sample by slave CPU"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x001E]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "PV1 current sample by slave CPU"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.1
+ rule: 1
+ registers: [0x001F]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Countdown time"
+ class: ""
+ state_class: "measurement"
+ uom: "s"
+ scale: 1
+ rule: 1
+ registers: [0x0020]
+ icon: ''
+
+ - name: "Inverter alert message"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0021]
+ icon: ''
+
+ - name: "Input mode"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0022]
+ icon: ''
+
+ - name: "Communication Board inner message"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0023]
+ icon: ''
+
+ - name: "Insulation of PV1+ to ground"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0024]
+ icon: ''
+
+ - name: "Insulation of PV2+ to ground"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0025]
+ icon: ''
+
+ - name: "Insulation of PV- to ground"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0026]
+ icon: ''
+
+ - name: "Country"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0027]
+ lookup:
+ - key: 0
+ value: "Germany"
+ - key: 1
+ value: "CEI0-21 Internal"
+ - key: 2
+ value: "Australia"
+ - key: 3
+ value: "Spain RD1699"
+ - key: 4
+ value: "Turkey"
+ - key: 5
+ value: "Denmark"
+ - key: 6
+ value: "Greece"
+ - key: 7
+ value: "Netherland"
+ - key: 8
+ value: "Belgium"
+ - key: 9
+ value: "UK-G59"
+ - key: 10
+ value: "China"
+ - key: 11
+ value: "France"
+ - key: 12
+ value: "Poland"
+ - key: 13
+ value: "Germany BDEW"
+ - key: 14
+ value: "Germany VDE0126"
+ - key: 15
+ value: "Italy CEI0-16"
+ - key: 16
+ value: "UK-G83"
+ - key: 17
+ value: "Greece Islands"
+ - key: 18
+ value: "EU EN50438"
+ - key: 19
+ value: "EU EN61727"
+ - key: 20
+ value: "Korea"
+ - key: 21
+ value: "Sweden"
+ - key: 22
+ value: "Europe General"
+ - key: 23
+ value: "CEI0-21 External"
+ - key: 24
+ value: "Cyprus"
+ - key: 25
+ value: "India"
+ - key: 26
+ value: "Philippines"
+ - key: 27
+ value: "New Zeland"
+ - key: 28
+ value: "Reserve"
+ - key: 29
+ value: "Reserve"
+ icon: ''
+
+ - group: Alert
+ items:
+ - name: "Fault 1"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0001]
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: "ID01 Grid Over Voltage Protection"
+ - key: 2
+ value: "ID02 Grid Under Voltage Protection"
+ - key: 4
+ value: "ID03 Grid Over Frequency Protection"
+ - key: 8
+ value: "ID04 Grid Under Frequency Protection"
+ - key: 16
+ value: "ID05 PV Under Voltage Protection"
+ - key: 32
+ value: "ID06 Grid Low Voltage Ride through"
+ - key: 64
+ value: "ID07"
+ - key: 128
+ value: "ID08"
+ - key: 256
+ value: "ID09 PV Over Voltage Protection"
+ - key: 512
+ value: "ID10 PV Input Current Unbalanced"
+ - key: 1024
+ value: "ID11 PV Input Mode wrong configuration"
+ - key: 2048
+ value: "ID12 Ground-Fault circuit interrupters fault"
+ - key: 4096
+ value: "ID13 Phase sequence fault"
+ - key: 8192
+ value: "ID14 Hardware boost over current protection"
+ - key: 16384
+ value: "ID15 Hardware AC over current protection"
+ - key: 32768
+ value: "ID16 Grid current is too high"
+ icon: 'mdi:wrench'
+
+ - name: "Fault 2"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0002]
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: "ID17 Grid current sampling error"
+ - key: 2
+ value: "ID18 DCI sampling error"
+ - key: 4
+ value: "ID19 Grid voltage sampling error"
+ - key: 8
+ value: "ID20 GFCI device sampling error"
+ - key: 16
+ value: "ID21 Main chip fault"
+ - key: 32
+ value: "ID22 Hardware auxiliary power fault"
+ - key: 64
+ value: "ID23 Bus voltage zero fault"
+ - key: 128
+ value: "ID24 Output current not balanced"
+ - key: 256
+ value: "ID25 Bus under voltage protection"
+ - key: 512
+ value: "ID26 Bus over voltage protection"
+ - key: 1024
+ value: "ID27 Bus voltage unbalanced"
+ - key: 2048
+ value: "ID28 DCI is too high"
+ - key: 4096
+ value: "ID29 Grid current is too high"
+ - key: 8192
+ value: "ID30 Input current is too high"
+ - key: 16384
+ value: "ID31"
+ - key: 32768
+ value: "ID32"
+ icon: 'mdi:wrench'
+
+ - name: "Fault 3"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0003]
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: "ID33 Reserved"
+ - key: 2
+ value: "ID34 Reserved"
+ - key: 4
+ value: "ID35 Reserved"
+ - key: 8
+ value: "ID36 Reserved"
+ - key: 16
+ value: "ID37 Reserved"
+ - key: 32
+ value: "ID38 Reserved"
+ - key: 64
+ value: "ID39 Reserved"
+ - key: 128
+ value: "ID40 Reserved"
+ - key: 256
+ value: "ID41 Reserved"
+ - key: 512
+ value: "ID42 Reserved"
+ - key: 1024
+ value: "ID43 Reserved"
+ - key: 2048
+ value: "ID44 Reserved"
+ - key: 4096
+ value: "ID45 Reserved"
+ - key: 8192
+ value: "ID46 Reserved"
+ - key: 16384
+ value: "ID47 Reserved"
+ - key: 32768
+ value: "ID48 Reserved"
+ icon: 'mdi:wrench'
+
+ - name: "Fault 4"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0004]
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: "ID49 Grid voltage sampling value between master and slave DSP vary widely"
+ - key: 2
+ value: "ID50 Grid frequency sampling value between master and slave DSP vary widely"
+ - key: 4
+ value: "ID51 DCI sampling value between master and slave DSP vary widely"
+ - key: 8
+ value: "ID52 GFCI sampling value between master and slave DSP vary widely"
+ - key: 16
+ value: "ID53 Communication failure between master and slave DSP failure"
+ - key: 32
+ value: "ID53 Communication failure between slave and communication board"
+ - key: 64
+ value: "ID55 Relay fault"
+ - key: 128
+ value: "ID56 Insulation resistance between PV array and the earth is too low"
+ - key: 256
+ value: "ID57 Inverter temp is too high"
+ - key: 512
+ value: "ID58 Boost temp is too high"
+ - key: 1024
+ value: "ID59 Environment temp is too high"
+ - key: 2048
+ value: "ID60 Brak podłączenie falownika do kabla PE"
+ - key: 4096
+ value: "ID61 Reserved"
+ - key: 8192
+ value: "ID62 Reserved"
+ - key: 16384
+ value: "ID63 Reserved"
+ - key: 32768
+ value: "ID64 Reserved"
+ icon: 'mdi:wrench'
+
+ - name: "Fault 5"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0005]
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: "ID65 Grid current is too high and causes unrecoverable fault"
+ - key: 2
+ value: "ID66 Bus voltage is too high and causes unrecoverable fault"
+ - key: 4
+ value: "ID67 Grid current is unbalanced and causes unrecoverable fault"
+ - key: 8
+ value: "ID68 Input current is unbalanced and causes unrecoverable fault"
+ - key: 16
+ value: "ID69 Bus voltage is unbalanced and causes unrecoverable fault"
+ - key: 32
+ value: "ID70 Grid current is too high and causes unrecoverable fault"
+ - key: 64
+ value: "ID65 PV Input Mode Configuration is wrong and causes unrecoverable fault"
+ - key: 128
+ value: "ID72 Reserved"
+ - key: 256
+ value: "ID73 Reserved"
+ - key: 512
+ value: "ID74 Input current is too high and causes unrecoverable fault"
+ - key: 1024
+ value: "ID75 Error reading from EEPROM"
+ - key: 2048
+ value: "ID76 Error writing to EEPROM"
+ - key: 4096
+ value: "ID77 Relay fauilure causes unrecoverable fault"
+ - key: 8192
+ value: "ID78 Reserved"
+ - key: 16384
+ value: "ID79 Reserved"
+ - key: 32768
+ value: "ID80 Reserved"
+ icon: 'mdi:wrench'
diff --git a/bundles/org.openhab.binding.solarman/src/main/resources/definitions/sofar_wifikit.yaml b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/sofar_wifikit.yaml
new file mode 100644
index 00000000000..050233f07cd
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/sofar_wifikit.yaml
@@ -0,0 +1,769 @@
+requests:
+ # Inverter State
+ - start: 0x0200
+ end: 0x0245
+ mb_functioncode: 0x03
+ # Inverter Settings
+ - start: 0x10B0
+ end: 0x10BC
+ mb_functioncode: 0x04
+ # Inverter Information
+ - start: 0x2000
+ end: 0x200B
+ mb_functioncode: 0x04
+
+# Tested with Solarman ME3000-SP with an embedded
+# "wifikit" logger. Might work for other devices
+# too.
+
+# The ME3000-SP is basically a glorified battery
+# charger - it is not directly connected to any
+# generation infrastructure, but can calculate
+# generated energy based on any CT clamps it is
+# connected to. For most people this entity will
+# be a generally accurate representation of their
+# PV panel output, but since this tracks generation
+# from _any_ source, it is named generically.
+
+parameters:
+ - group: Generation
+ items:
+ - name: "Generation Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "kW"
+ scale: 0.01
+ rule: 1
+ registers: [0x0215]
+ icon: 'mdi:solar-power'
+
+ - name: "Daily Generation"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.01
+ rule: 1
+ registers: [0x0218]
+ icon: 'mdi:solar-power'
+
+ - name: "Total Generation"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 1
+ rule: 3
+ registers: [0x021D,0x021C]
+ icon: 'mdi:solar-power'
+
+ - name: "Daily Generation Time"
+ class: "duration"
+ state_class: "total_increasing"
+ uom: "min"
+ scale: 1
+ rule: 1
+ registers: [0x0243]
+ icon: 'mdi:sun-clock-outline'
+
+ - name: "Total Generation Time"
+ class: "duration"
+ state_class: "total_increasing"
+ uom: "h"
+ scale: 1
+ rule: 3
+ registers: [0x0245,0x244]
+ icon: 'mdi:sun-clock-outline'
+
+ - group: Load
+ items:
+ - name: "Consumption Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "kW"
+ scale: 0.01
+ rule: 1
+ registers: [0x0213]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Daily Consumption"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.01
+ rule: 1
+ registers: [0x021B]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Total Consumption"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 1
+ rule: 3
+ registers: [0x0223,0x0222]
+ icon: 'mdi:home-lightning-bolt-outline'
+
+ - group: Grid
+ items:
+ - name: "Grid A Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x0206]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Grid A Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 2
+ registers: [0x0207]
+ icon: 'mdi:current-ac'
+
+ - name: "Grid B Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x0208]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Grid B Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 2
+ registers: [0x0209]
+ icon: 'mdi:current-ac'
+
+ - name: "Grid C Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x020A]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Grid C Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 2
+ registers: [0x020B]
+ icon: 'mdi:current-ac'
+
+ - name: "Grid Frequency"
+ class: "frequency"
+ state_class: "measurement"
+ uom: "Hz"
+ scale: 0.01
+ rule: 1
+ registers: [0x20C]
+ icon: 'mdi:sine-wave'
+
+ - name: "Daily Power Sold"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.01
+ rule: 1
+ registers: [0x0219]
+ icon: 'mdi:transmission-tower-export'
+
+ - name: "Daily Power Bought"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.01
+ rule: 1
+ registers: [0x021A]
+ icon: 'mdi:transmission-tower-import'
+
+ - group: Battery
+ items:
+ - name: "Battery Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.01
+ rule: 1
+ registers: [0x020E]
+ icon: 'mdi:home-battery'
+
+ - name: "Battery Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 2
+ registers: [0x020F]
+ icon: 'mdi:current-dc'
+
+ - name: "Battery Charge / Discharge Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "kW"
+ scale: 0.01
+ rule: 2
+ registers: [0x020D]
+ icon: 'mdi:home-battery'
+
+ - name: "Battery SOC"
+ class: "battery"
+ state_class: "measurement"
+ uom: "%"
+ scale: 1
+ rule: 1
+ registers: [0x210]
+ icon: 'mdi:battery'
+
+ - name: "Battery Temperature"
+ class: "temperature"
+ state_class: "measurement"
+ uom: "°C"
+ scale: 1
+ rule: 1
+ registers: [0x0211]
+ icon: 'mdi:thermometer'
+
+ - name: "Battery Capacity"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x10B1]
+ icon: 'mdi:battery-high'
+
+ - name: "Battery Max Charge Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x10B3]
+ icon: 'mdi:battery'
+
+ - name: "Battery Max Charge Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 2
+ registers: [0x10B4]
+ icon: 'mdi:current-dc'
+
+ - name: "Battery Min Discharge Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x10B6]
+ icon: 'mdi:battery'
+
+ - name: "Battery Max Discharge Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 2
+ registers: [0x10B7]
+ icon: 'mdi:current-dc'
+
+ - name: "Battery Discharge Depth"
+ class: "battery"
+ state_class: "measurement"
+ uom: "%"
+ scale: 1
+ rule: 1
+ registers: [0x10B9]
+ icon: 'mdi:battery-high'
+
+ - name: "Battery Empty Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.01
+ rule: 1
+ registers: [0x10BB]
+ icon: 'mdi:battery'
+
+ - name: "Battery Full Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.01
+ rule: 1
+ registers: [0x10BC]
+ icon: 'mdi:battery'
+
+ - name: "Battery Type"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x10B0]
+ # This field has different definitions
+ # depending on a "Version". There is no
+ # indication what version this is,
+ # whether it is hardware or software so
+ # this is the V1.00 list. If your battery
+ # type is detected incorrectly, you
+ # probably have V1.20 (whatever that is)
+ # and need to use a custom inverter
+ # definition with the commented lookups
+ # below.
+ lookup:
+ - key: 0x0000
+ value: "DARFON"
+ - key: 0x0001
+ value: "PYLON"
+ - key: 0x0003
+ value: "SOLTARO"
+ - key: 0x0080
+ value: "TELE"
+ - key: 0x0100
+ value: "DEFAULT"
+
+ # V1.20 Lookups
+ # lookup:
+ # - key: 0x0000
+ # value: "DARFON"
+ # - key: 0x0001
+ # value: "PYLON"
+ # - key: 0x0002
+ # value: "SOLTARO"
+ # - key: 0x0003
+ # value: "ALPHA.ESS"
+ # - key: 0x0004
+ # value: "GENERAL"
+ # - key: 0x0100
+ # value: "DEFAULT"
+
+ - group: InverterStatus
+ items:
+ - name: "Inverter Status"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x200]
+ lookup:
+ - key: 0
+ value: "Wait"
+ - key: 1
+ value: "Self Check"
+ - key: 2
+ value: "Charging"
+ - key: 3
+ value: "Check Discharge"
+ - key: 4
+ value: "Discharging"
+ - key: 5
+ value: "EPS"
+ - key: 6
+ value: "Fault"
+ - key: 7
+ value: "Permanent Fault"
+ icon: 'mdi:wrench'
+
+ - name: "Inverter Temperature"
+ class: "temperature"
+ state_class: "measurement"
+ uom: "°C"
+ scale: 1
+ rule: 1
+ registers: [0x0238]
+ icon: 'mdi:thermometer'
+
+ - name: "Inverter Heat-Sink Temperature"
+ class: "temperature"
+ state_class: "measurement"
+ uom: "°C"
+ scale: 1
+ rule: 1
+ registers: [0x0239]
+ icon: 'mdi:thermometer'
+
+ - name: "Inverter Bus Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x022D]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "LLC Bus Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [0x022E]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Countdown Time"
+ class: ""
+ state_class: "measurement"
+ uom: "s"
+ scale: 1
+ rule: 1
+ registers: [0x022A]
+ icon: ''
+
+ - name: "Inverter Alert Message"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x022B]
+ icon: ''
+
+ - name: "Communication Board Inner Message"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0242]
+ icon: ''
+
+ - name: "Country"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x023A]
+ lookup:
+ - key: 0
+ value: "Germany"
+ - key: 1
+ value: "CEI0-21 Internal"
+ - key: 2
+ value: "Australia"
+ - key: 3
+ value: "Spain RD1699"
+ - key: 4
+ value: "Turkey"
+ - key: 5
+ value: "Denmark"
+ - key: 6
+ value: "Greece"
+ - key: 7
+ value: "Netherland"
+ - key: 8
+ value: "Belgium"
+ - key: 9
+ value: "UK-G59"
+ - key: 10
+ value: "China"
+ - key: 11
+ value: "France"
+ - key: 12
+ value: "Poland"
+ - key: 13
+ value: "Germany BDEW"
+ - key: 14
+ value: "Germany VDE0126"
+ - key: 15
+ value: "Italy CEI0-16"
+ - key: 16
+ value: "UK-G83"
+ - key: 17
+ value: "Greece Islands"
+ - key: 18
+ value: "EU EN50438"
+ - key: 19
+ value: "EU EN61727"
+ - key: 20
+ value: "Korea"
+ - key: 21
+ value: "Sweden"
+ - key: 22
+ value: "Europe General"
+ - key: 23
+ value: "CEI0-21 External"
+ - key: 24
+ value: "Cyprus"
+ - key: 25
+ value: "India"
+ - key: 26
+ value: "Philippines"
+ - key: 27
+ value: "New Zeland"
+ - key: 28
+ value: "Reserve"
+ - key: 29
+ value: "Reserve"
+ icon: ''
+
+ - group: Alert
+ items:
+ - name: "Fault 1"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0201]
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: "ID01 Grid Over Voltage Protection"
+ - key: 2
+ value: "ID02 Grid Under Voltage Protection"
+ - key: 4
+ value: "ID03 Grid Over Frequency Protection"
+ - key: 8
+ value: "ID04 Grid Under Frequency Protection"
+ - key: 16
+ value: "ID05 PV Under Voltage Protection"
+ - key: 32
+ value: "ID06 Grid Low Voltage Ride through"
+ - key: 64
+ value: "ID07"
+ - key: 128
+ value: "ID08"
+ - key: 256
+ value: "ID09 PV Over Voltage Protection"
+ - key: 512
+ value: "ID10 PV Input Current Unbalanced"
+ - key: 1024
+ value: "ID11 PV Input Mode wrong configuration"
+ - key: 2048
+ value: "ID12 Ground-Fault circuit interrupters fault"
+ - key: 4096
+ value: "ID13 Phase sequence fault"
+ - key: 8192
+ value: "ID14 Hardware boost over current protection"
+ - key: 16384
+ value: "ID15 Hardware AC over current protection"
+ - key: 32768
+ value: "ID16 Grid current is too high"
+ icon: 'mdi:wrench'
+
+ - name: "Fault 2"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0202]
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: "ID17 Grid current sampling error"
+ - key: 2
+ value: "ID18 DCI sampling error"
+ - key: 4
+ value: "ID19 Grid voltage sampling error"
+ - key: 8
+ value: "ID20 GFCI device sampling error"
+ - key: 16
+ value: "ID21 Main chip fault"
+ - key: 32
+ value: "ID22 Hardware auxiliary power fault"
+ - key: 64
+ value: "ID23 Bus voltage zero fault"
+ - key: 128
+ value: "ID24 Output current not balanced"
+ - key: 256
+ value: "ID25 Bus under voltage protection"
+ - key: 512
+ value: "ID26 Bus over voltage protection"
+ - key: 1024
+ value: "ID27 Bus voltage unbalanced"
+ - key: 2048
+ value: "ID28 DCI is too high"
+ - key: 4096
+ value: "ID29 Grid current is too high"
+ - key: 8192
+ value: "ID30 Input current is too high"
+ - key: 16384
+ value: "ID31"
+ - key: 32768
+ value: "ID32"
+ icon: 'mdi:wrench'
+
+ - name: "Fault 3"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0203]
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: "ID33 Reserved"
+ - key: 2
+ value: "ID34 Reserved"
+ - key: 4
+ value: "ID35 Reserved"
+ - key: 8
+ value: "ID36 Reserved"
+ - key: 16
+ value: "ID37 Reserved"
+ - key: 32
+ value: "ID38 Reserved"
+ - key: 64
+ value: "ID39 Reserved"
+ - key: 128
+ value: "ID40 Reserved"
+ - key: 256
+ value: "ID41 Reserved"
+ - key: 512
+ value: "ID42 Reserved"
+ - key: 1024
+ value: "ID43 Reserved"
+ - key: 2048
+ value: "ID44 Reserved"
+ - key: 4096
+ value: "ID45 Reserved"
+ - key: 8192
+ value: "ID46 Reserved"
+ - key: 16384
+ value: "ID47 Reserved"
+ - key: 32768
+ value: "ID48 Reserved"
+ icon: 'mdi:wrench'
+
+ - name: "Fault 4"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0204]
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: "ID49 Grid voltage sampling value between master and slave DSP vary widely"
+ - key: 2
+ value: "ID50 Grid frequency sampling value between master and slave DSP vary widely"
+ - key: 4
+ value: "ID51 DCI sampling value between master and slave DSP vary widely"
+ - key: 8
+ value: "ID52 GFCI sampling value between master and slave DSP vary widely"
+ - key: 16
+ value: "ID53 Communication failure between master and slave DSP failure"
+ - key: 32
+ value: "ID53 Communication failure between slave and communication board"
+ - key: 64
+ value: "ID55 Relay fault"
+ - key: 128
+ value: "ID56 Insulation resistance between PV array and the earth is too low"
+ - key: 256
+ value: "ID57 Inverter temp is too high"
+ - key: 512
+ value: "ID58 Boost temp is too high"
+ - key: 1024
+ value: "ID59 Environment temp is too high"
+ - key: 2048
+ value: "ID60 Brak podłączenie falownika do kabla PE"
+ - key: 4096
+ value: "ID61 Reserved"
+ - key: 8192
+ value: "ID62 Reserved"
+ - key: 16384
+ value: "ID63 Reserved"
+ - key: 32768
+ value: "ID64 Reserved"
+ icon: 'mdi:wrench'
+
+ - name: "Fault 5"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x0205]
+ lookup:
+ - key: 0
+ value: "No error"
+ - key: 1
+ value: "ID65 Grid current is too high and causes unrecoverable fault"
+ - key: 2
+ value: "ID66 Bus voltage is too high and causes unrecoverable fault"
+ - key: 4
+ value: "ID67 Grid current is unbalanced and causes unrecoverable fault"
+ - key: 8
+ value: "ID68 Input current is unbalanced and causes unrecoverable fault"
+ - key: 16
+ value: "ID69 Bus voltage is unbalanced and causes unrecoverable fault"
+ - key: 32
+ value: "ID70 Grid current is too high and causes unrecoverable fault"
+ - key: 64
+ value: "ID65 PV Input Mode Configuration is wrong and causes unrecoverable fault"
+ - key: 128
+ value: "ID72 Reserved"
+ - key: 256
+ value: "ID73 Reserved"
+ - key: 512
+ value: "ID74 Input current is too high and causes unrecoverable fault"
+ - key: 1024
+ value: "ID75 Error reading from EEPROM"
+ - key: 2048
+ value: "ID76 Error writing to EEPROM"
+ - key: 4096
+ value: "ID77 Relay fauilure causes unrecoverable fault"
+ - key: 8192
+ value: "ID78 Reserved"
+ - key: 16384
+ value: "ID79 Reserved"
+ - key: 32768
+ value: "ID80 Reserved"
+ icon: 'mdi:wrench'
+
+ - group: InverterInformation
+ items:
+ - name: "Production Code"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [0x2000]
+
+ - name: "Serial Number"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 5
+ registers: [0x2001,0x2002,0x2003,0x2004,0x2005,0x2006,0x2007]
+ isstr: true
+
+ - name: "Software Version"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 5
+ registers: [0x2008,0x2009]
+ isstr: true
+
+ - name: "Hardware Version"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 5
+ registers: [0x200A,0x200B]
+ isstr: true
\ No newline at end of file
diff --git a/bundles/org.openhab.binding.solarman/src/main/resources/definitions/solis_1p8k-5g.yaml b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/solis_1p8k-5g.yaml
new file mode 100644
index 00000000000..22ca58d3652
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/solis_1p8k-5g.yaml
@@ -0,0 +1,204 @@
+# Solis Single Phase Inverter
+# 1P8K-5G
+# Modbus information derived by test and comparing to Solis Cloud
+# Gedger V.0.1 May 2022
+#
+requests:
+ - start: 2999
+ end: 3024
+ mb_functioncode: 0x04
+ - start: 3035
+ end: 3043
+ mb_functioncode: 0x04
+ - start: 3071
+ end: 3071
+ mb_functioncode: 0x04
+
+parameters:
+ - group: InverterStatus
+ items:
+ - name: "Inverter Status"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 6
+ registers: [3043]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Operating Status"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 6
+ registers: [3071]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Inverter Temperature"
+ class: "temperature"
+ state_class: "measurement"
+ uom: "°C"
+ scale: 0.1
+ rule: 2
+ registers: [3041]
+ icon: 'mdi:thermometer'
+
+# - name: "Inverter ID"
+# class: ""
+# state_class: ""
+# uom: ""
+# scale: 1
+# rule: 5
+# registers: [33004,33005,33006,33007,33008,33009,33010,33011,33012,33013,33014,33015,33016,33017,33018,33019]
+# isstr: true
+
+ - name: "Product Model"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 6
+ registers: [2999]
+ isstr: true
+
+ - name: "DSP Software Version"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 6
+ registers: [3000]
+ isstr: true
+
+ - name: "LCD Software Version"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 6
+ registers: [3001]
+ isstr: true
+
+ - group: InverterDC
+ items:
+ - name: "PV1 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [3021]
+ icon: 'mdi:solar-power'
+
+ - name: "PV2 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [3023]
+ icon: 'mdi:solar-power'
+
+ - name: "PV1 Current"
+ class: "current"
+ uom: "A"
+ scale: 0.1
+ rule: 1
+ registers: [3022]
+ icon: 'mdi:current-dc'
+
+ - name: "PV2 Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.1
+ rule: 1
+ registers: [3024]
+ icon: 'mdi:current-dc'
+
+ - name: "Total DC Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "kW"
+ scale: 0.001
+ rule: 3
+ registers: [3007, 3006]
+ icon: 'mdi:solar-power'
+
+ - group: InverterAC
+ items:
+ - name: "Inverter AC Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "kW"
+ scale: 0.001
+ rule: 3
+ registers: [3005, 3004]
+ icon: 'mdi:solar-power'
+
+ - name: "Inverter Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [3035]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Inverter Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.1
+ rule: 1
+ registers: [3038]
+ icon: 'mdi:current-ac'
+
+ - name: "Inverter Frequency"
+ class: "frequency"
+ state_class: "measurement"
+ uom: "Hz"
+ scale: 0.01
+ rule: 1
+ registers: [3042]
+ icon: 'mdi:sine-wave'
+
+ - group: Generation
+ items:
+ - name: "Daily Generation"
+ class: "energy"
+ state_class: "measurement"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [3014]
+ icon: 'mdi:solar-power'
+
+ - name: "Monthly Generation"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 1
+ rule: 3
+ registers: [3011, 3010]
+ icon: 'mdi:solar-power'
+
+ - name: "Yearly Generation"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 1
+ rule: 3
+ registers: [3017, 3016]
+ icon: 'mdi:solar-power'
+
+ - name: "Total Generation"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 1
+ rule: 3
+ registers: [3009, 3008]
+ icon: 'mdi:solar-power'
+
diff --git a/bundles/org.openhab.binding.solarman/src/main/resources/definitions/solis_hybrid.yaml b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/solis_hybrid.yaml
new file mode 100644
index 00000000000..78b24eea7ab
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/solis_hybrid.yaml
@@ -0,0 +1,783 @@
+# Solis Single Phase Hybrid
+# RHI-(3-6)K-48ES-5G
+# Modbus information retrieved from:
+# https://www.scss.tcd.ie/coghlan/Elios4you/RS485_MODBUS-Hybrid-BACoghlan-201811228-1854.pdf
+
+requests:
+ - start: 33029
+ end: 33095
+ mb_functioncode: 0x04
+ - start: 33116
+ end: 33179
+ mb_functioncode: 0x04
+ - start: 33206
+ end: 33282
+ mb_functioncode: 0x04
+
+parameters:
+ - group: InverterStatus
+ items:
+ - name: "Inverter Status"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [33095]
+ icon: 'mdi:home-lightning-bolt'
+ lookup:
+ - key: 0x0
+ value: "Waiting State"
+ - key: 0x1
+ value: "Open Loop Operation"
+ - key: 0x2
+ value: "Soft Start"
+ - key: 0x3
+ value: "On Grid/Generating"
+ - key: 0x1004
+ value: "Grid OverVoltage"
+ - key: 0x1010
+ value: "Grid UnderVoltage"
+ - key: 0x1012
+ value: "Grid OverFrequency"
+ - key: 0x1013
+ value: "Grid UnderFrequency"
+ - key: 0x1014
+ value: "Grid Imp too large"
+ - key: 0x1015
+ value: "No Grid"
+ - key: 0x1016
+ value: "Grid Imbalance"
+ - key: 0x1017
+ value: "Grid Freq Jitter"
+ - key: 0x1018
+ value: "Grid Overcurrent"
+ - key: 0x1019
+ value: "Grid Tracking Fault"
+ - key: 0x1020
+ value: "DC OverVoltage"
+ - key: 0x1021
+ value: "DC Bus Overvoltage"
+ - key: 0x1022
+ value: "DC Bus Uneven Voltage"
+ - key: 0x1024
+ value: "DC Bus Uneven Voltage2"
+ - key: 0x1025
+ value: "DC A path OverCurrent"
+ - key: 0x1026
+ value: "DC B path OverCurrent"
+ - key: 0x1027
+ value: "DC Input Disturbance"
+ - key: 0x1030
+ value: "Grid Disturbance"
+ - key: 0x1031
+ value: "DSP Initialization Protection "
+ - key: 0x1032
+ value: "Over Temp Protection"
+ - key: 0x1033
+ value: "PV Insulation Fault"
+ - key: 0x1034
+ value: "Leakage Current Protection"
+ - key: 0x1035
+ value: "Relay Detection Protection"
+ - key: 0x1036
+ value: "DSP_B Protection"
+ - key: 0x1037
+ value: "DC Component too Large"
+ - key: 0x1038
+ value: "12v UnderVoltage Protection"
+ - key: 0x1039
+ value: "Under Temperature Protection"
+ - key: 0x1040
+ value: "Arc Self-Test Protection"
+ - key: 0x1041
+ value: "Arc Protection"
+ - key: 0x1042
+ value: "DSP on-chip SRAM exception"
+ - key: 0x1043
+ value: "DSP on-chip FLASH exception"
+ - key: 0x1044
+ value: "DSP on-chip PC pointer is abnormal"
+ - key: 0x1045
+ value: "DSP key register exception"
+ - key: 0x1046
+ value: "Grid disturbance 02"
+ - key: 0x1047
+ value: "Grid current sampling abnormality"
+ - key: 0x1048
+ value: "IGBT overcurrent"
+ - key: 0x1050
+ value: "Network current transient overcurrent"
+ - key: 0x1051
+ value: "Battery overvoltage hardware failure"
+ - key: 0x1052
+ value: "LLC hardware overcurrent"
+ - key: 0x1053
+ value: "Battery overvoltage detection"
+ - key: 0x1054
+ value: "Battery undervoltage detection"
+ - key: 0x1055
+ value: "Battery no connected"
+ - key: 0x1056
+ value: "Bypass overvoltage fault"
+ - key: 0x1057
+ value: "Bypass overload fault"
+
+ - name: "Operating Status"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [33121]
+ icon: 'mdi:home-lightning-bolt'
+ lookup:
+ - key: 0x701
+ value: "Normal Operation"
+ - key: 0x702
+ value: "Initial Standby"
+ - key: 0x704
+ value: "Control Shutdown"
+ - key: 0x708
+ value: "Downtime"
+ - key: 0x710
+ value: "Standby"
+ - key: 0x720
+ value: "Derating Operation"
+ - key: 0x740
+ value: "Limit Operation"
+ - key: 0x780
+ value: "Bypass Overload"
+
+ - name: "Grid Fault Status"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [33116]
+ icon: 'mdi:alert'
+ lookup:
+ - key: 0x0000
+ value: "No Fault"
+ - key: 0x1
+ value: "No Grid"
+ - key: 0x2
+ value: "Grid OverVoltage"
+ - key: 0x4
+ value: "Grid UnderVoltage"
+ - key: 0x8
+ value: "Grid OverFrequency"
+ - key: 0x10
+ value: "Grid UnderFrequency"
+ - key: 0x20
+ value: "Grid Imbalance"
+ - key: 0x40
+ value: "Grid Frequncy Jitter"
+ - key: 0x80
+ value: "Grid Impedence too Large"
+ - key: 0x100
+ value: "Grid Tracking Fault"
+ - key: 0x200
+ value: "Meter Comm Failure"
+ - key: 0x400
+ value: "Failsafe"
+
+ - name: "Backup Load Fault Status"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [33117]
+ icon: 'mdi:alert'
+ lookup:
+ - key: 0x0
+ value: "No Fault"
+ - key: 0x1
+ value: "Bypass OverVoltage Fault"
+ - key: 0x2
+ value: "Bypass Overload Fault"
+ - name: "Battery Fault Status"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [33118]
+ icon: 'mdi:alert'
+ lookup:
+ - key: 0x0
+ value: "No Fault"
+ - key: 0x1
+ value: "Battery Not Connected"
+ - key: 0x2
+ value: "Battery OverVoltage Detection"
+ - key: 0x4
+ value: "Battery UnderVoltage Detection"
+
+ - name: "Fault Status 04 (Device)"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [33119]
+ icon: 'mdi:alert'
+ lookup:
+ - key: 0x0000
+ value: "No Fault"
+ - key: 0x1
+ value: "DC OverVoltage"
+ - key: 0x2
+ value: "DC Bus OverVoltage"
+ - key: 0x4
+ value: "DC Bus Uneven Voltage"
+ - key: 0x8
+ value: "DC Bus UnderVoltage"
+ - key: 0x10
+ value: "DC Bus2 Uneven Voltage"
+ - key: 0x20
+ value: "DC A path OverCurrent"
+ - key: 0x40
+ value: "DC B path OverCurrent"
+ - key: 0x80
+ value: "DC Input Disturbance"
+ - key: 0x100
+ value: "Grid OverCurrent"
+ - key: 0x200
+ value: "IGBT OverCurrent"
+ - key: 0x400
+ value: "Grid Disturbance 2"
+ - key: 0x800
+ value: "Arc Self-Test Protection"
+ - key: 0x1000
+ value: "Arc Fault Reservation"
+ - key: 0x2000
+ value: "Grid Current Sample Abnormality"
+ - name: "Fault Status 05 (Device)"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [33120]
+ icon: 'mdi:alert'
+ lookup:
+ - key: 0x0000
+ value: "No Fault"
+ - key: 0x1
+ value: "Grid Disturbance"
+ - key: 0x2
+ value: "DC Component Too Large"
+ - key: 0x4
+ value: "Over Temp Protection"
+ - key: 0x8
+ value: "Relay Detection Protection"
+ - key: 0x10
+ value: "Under Temp Protection"
+ - key: 0x20
+ value: "PV Insulation Fault"
+ - key: 0x40
+ value: "12V UnderVoltage Protection"
+ - key: 0x80
+ value: "Leakage Current Protection"
+ - key: 0x100
+ value: "Leakage Current Self-Test"
+ - key: 0x200
+ value: "DSP Initialization Protect"
+ - key: 0x400
+ value: "DSP B Protection"
+ - key: 0x800
+ value: "Battery Overvoltage H/W Failure"
+ - key: 0x1000
+ value: "LLC Hardware OverCurrent"
+ - key: 0x2000
+ value: "Network Side Transient OverCurrent"
+ - key: 0x4000
+ value: "CAN Communication Failed"
+ - key: 0x8000
+ value: "DSP Communication Failed"
+ - name: "Inverter Temperature"
+ class: "temperature"
+ state_class: "measurement"
+ uom: "°C"
+ scale: 0.1
+ rule: 2
+ registers: [33093]
+ icon: 'mdi:thermometer'
+
+# Sensors below are outside of modbus request ranges.
+# If enabling, ensure to amend the request start register.
+#
+# - name: "Inverter ID"
+# class: ""
+# state_class: ""
+# uom: ""
+# scale: 1
+# rule: 5
+# registers: [33004,33005,33006,33007,33008,33009,33010,33011,33012,33013,33014,33015,33016,33017,33018,33019]
+# isstr: true
+
+# - name: "Product Model"
+# class: ""
+# state_class: ""
+# uom: ""
+# scale: 1
+# rule: 6
+# registers: [33000]
+# isstr: true
+
+# - name: "DSP Software Version"
+# class: ""
+# state_class: ""
+# uom: ""
+# scale: 1
+# rule: 6
+# registers: [33001]
+# isstr: true
+
+# - name: "LCD Software Version"
+# class: ""
+# state_class: ""
+# uom: ""
+# scale: 1
+# rule: 6
+# registers: [33002]
+# isstr: true
+
+# - name: "Protocol Software Version"
+# class: ""
+# state_class: ""
+# uom: ""
+# scale: 1
+# rule: 6
+# registers: [33003]
+# isstr: true
+
+ - name: "Storage Control Mode"
+ class: ""
+ state_class: ""
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [33132]
+ icon: 'mdi:battery-clock'
+ lookup:
+ - key: 0x21
+ value: "Spontaneous Mode"
+ - key: 0x22
+ value: "Optimized Revenue Mode"
+ - key: 0x23
+ value: "Charging from Grid"
+ - key: 0x24
+ value: "Off-Grid Storage Mode"
+ - key: 0x28
+ value: "Battery Wake-Up"
+ - group: InverterDC
+ items:
+ - name: "PV1 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [33049]
+ icon: 'mdi:solar-power'
+
+ - name: "PV2 Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [33051]
+ icon: 'mdi:solar-power'
+
+ - name: "PV1 Current"
+ class: "current"
+ uom: "A"
+ scale: 0.1
+ rule: 1
+ registers: [33050]
+ icon: 'mdi:current-dc'
+
+ - name: "PV2 Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.1
+ rule: 1
+ registers: [33052]
+ icon: 'mdi:current-dc'
+
+ - name: "Inverter DC Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 3
+ registers: [33058,33057]
+ icon: 'mdi:solar-power'
+
+ - group: InverterAC
+ items:
+ - name: "Inverter AC Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 4
+ registers: [33152,33151]
+ icon: 'mdi:solar-power'
+
+ - name: "Inverter Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [33073]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Inverter Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.1
+ rule: 1
+ registers: [33076]
+ icon: 'mdi:current-ac'
+
+ - name: "Inverter Active Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 4
+ registers: [33080,33079]
+ icon: 'mdi:transmission-tower'
+
+ # Inverter Reactive Power is defined as a signed 32 bit integer
+ # across 33082 and 33081, however the field appears to be only
+ # 20 bits wide i.e. the upper 12 bits are always zero.
+ # Define only the lower signed 16 bits for moment
+ - name: "Inverter Reactive Power"
+ class: "reactive_power"
+ state_class: "measurement"
+ uom: "var"
+ scale: 1
+ rule: 4
+ registers: [33082]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Inverter Apparent Power"
+ class: "apparent_power"
+ state_class: "measurement"
+ uom: "VA"
+ scale: 1
+ rule: 4
+ registers: [33084,33083]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Inverter Frequency"
+ class: "frequency"
+ state_class: "measurement"
+ uom: "Hz"
+ scale: 0.01
+ rule: 1
+ registers: [33094]
+ icon: 'mdi:sine-wave'
+
+ - group: Generation
+ items:
+ - name: "Daily Generation"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [33035]
+ icon: 'mdi:solar-power'
+
+ - name: "Monthly Generation"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 1
+ rule: 3
+ registers: [33032,33031]
+ icon: 'mdi:solar-power'
+
+ - name: "Yearly Generation"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 1
+ rule: 3
+ registers: [33038,33037]
+ icon: 'mdi:solar-power'
+
+ - name: "Total Generation"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 1
+ rule: 3
+ registers: [33030,33029]
+ icon: 'mdi:solar-power'
+
+ - group: Grid
+ items:
+ - name: "Meter Frequency"
+ class: "frequency"
+ state_class: "measurement"
+ uom: "Hz"
+ scale: 0.01
+ rule: 1
+ registers: [33282]
+ icon: 'mdi:sine-wave'
+
+ - name: "Meter Power Factor"
+ class: "power_factor"
+ state_class: "measurement"
+ uom: "%"
+ scale: 0.01
+ rule: 2
+ registers: [33281]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Meter Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [33251]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Meter Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.01
+ rule: 1
+ registers: [33252]
+ icon: 'mdi:current-ac'
+
+ - name: "Meter Active Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 4
+ registers: [33258,33257]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Meter Reactive Power"
+ class: "reactive_power"
+ state_class: "measurement"
+ uom: "var"
+ scale: 1
+ rule: 4
+ registers: [33266,33265]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Meter Apparent Power"
+ class: "apparent_power"
+ state_class: "measurement"
+ uom: "VA"
+ scale: 1
+ rule: 4
+ registers: [33274,33273]
+ icon: 'mdi:transmission-tower'
+
+ - name: "Daily Energy Imported"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [33171]
+ icon: 'mdi:home-import-outline'
+
+ - name: "Total Energy Imported"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 1
+ rule: 3
+ registers: [33170,33169]
+ icon: 'mdi:home-import-outline'
+
+ - name: "Daily Energy Exported"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [33175]
+ icon: 'mdi:home-export-outline'
+
+ - name: "Total Energy Exported"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 1
+ rule: 3
+ registers: [33174,33173]
+ icon: 'mdi:home-export-outline'
+
+ - group: Load
+ items:
+ - name: "House Load Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 1
+ registers: [33147]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: "Backup Load Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 1
+ registers: [33148]
+ icon: 'mdi:home-battery'
+
+ - name: "Daily House+Backup Load Consumption"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [33179]
+ icon: 'mdi:lightning-bolt-outline'
+
+ - name: "Total House+Backup Load Consumption"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 1
+ rule: 3
+ registers: [33178,33177]
+ icon: 'mdi:lightning-bolt-outline'
+
+ - group: Battery
+ items:
+ - name: "Battery Status"
+ class: ""
+ state_class: "measurement"
+ uom: ""
+ scale: 1
+ rule: 1
+ registers: [33135]
+ isstr: true
+ lookup:
+ - key: 0
+ value: "Charge"
+ - key: 1
+ value: "Discharge"
+ icon: 'mdi:battery'
+
+ - name: "Battery Power"
+ class: "power"
+ state_class: "measurement"
+ uom: "W"
+ scale: 1
+ rule: 4
+ registers: [33150,33149]
+ icon: 'mdi:battery-charging'
+
+ - name: "Battery SOC"
+ class: "battery"
+ state_class: "measurement"
+ uom: "%"
+ scale: 1
+ rule: 1
+ registers: [33139]
+ icon: 'mdi:battery'
+
+ - name: "Battery SOH"
+ class: "battery"
+ state_class: "measurement"
+ uom: "%"
+ scale: 1
+ rule: 1
+ registers: [33140]
+ icon: 'mdi:battery'
+
+ - name: "Battery Current"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.1
+ rule: 2
+ registers: [33134]
+ icon: 'mdi:current-dc'
+
+ - name: "Battery Voltage"
+ class: "voltage"
+ state_class: "measurement"
+ uom: "V"
+ scale: 0.1
+ rule: 1
+ registers: [33133]
+ icon: 'mdi:battery'
+
+ - name: "Today Battery Charge"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [33163]
+ icon: 'mdi:battery-plus'
+
+ - name: "Today Battery Discharge"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 0.1
+ rule: 1
+ registers: [33167]
+ icon: 'mdi:battery-minus'
+
+ - name: "Total Battery Charge"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 1
+ rule: 3
+ registers: [33162,33161]
+ icon: 'mdi:battery-plus'
+
+ - name: "Total Battery Discharge"
+ class: "energy"
+ state_class: "total_increasing"
+ uom: "kWh"
+ scale: 1
+ rule: 3
+ registers: [33166,33165]
+ icon: 'mdi:battery-minus'
+
+ - name: "Battery Charge Current Limit"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.1
+ rule: 1
+ registers: [33206]
+ icon: 'mdi:battery-arrow-up'
+
+ - name: "Battery Discharge Current Limit"
+ class: "current"
+ state_class: "measurement"
+ uom: "A"
+ scale: 0.1
+ rule: 1
+ registers: [33207]
+ icon: 'mdi:battery-arrow-down'
diff --git a/bundles/org.openhab.binding.solarman/src/main/resources/definitions/zcs_azzurro-ktl-v3.yaml b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/zcs_azzurro-ktl-v3.yaml
new file mode 100644
index 00000000000..3ce74e0b668
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/main/resources/definitions/zcs_azzurro-ktl-v3.yaml
@@ -0,0 +1,745 @@
+# ZCS Azzurro 3-phase non-hybrid inverters
+# with LSW-3 WiFi logger with SN 23xxxxxxxx and FW LSW3_15_270A_1.53:
+# 3PH 3.3KTL-V3
+# 3PH 4.4KTL-V3
+# 3PH 5.5KTL-V3
+# 3PH 6.6KTL-V3
+
+# Not tested, but could probably work:
+# ZCS Azzurro 3PH 8.8KTL-V3
+# ZCS Azzurro 3PH 11KTL-V3
+# ZCS Azzurro 3PH 12KTL-V3
+# SOFAR Solar 4.4KTLX-G3
+# SOFAR Solar 5.5KTLX-G3
+# SOFAR Solar 6.6KTLX-G3
+# SOFAR Solar 8.8KTLX-G3
+# SOFAR Solar 11KTLX-G3
+# SOFAR Solar 12KTLX-G3
+
+requests:
+ - start: 0x0400
+ end: 0x042B
+ mb_functioncode: 0x03
+ - start: 0x0482
+ end: 0x04A4
+ mb_functioncode: 0x03
+ - start: 0x0582
+ end: 0x0589
+ mb_functioncode: 0x03
+ - start: 0x0682
+ end: 0x068B
+ mb_functioncode: 0x03
+
+parameters:
+ - group: Solar
+ items:
+ - name: 'PV Generation today'
+ class: 'energy'
+ state_class: 'total'
+ uom: 'kWh'
+ scale: 0.01
+ rule: 1
+ registers: [0x0685, 0x0684]
+ icon: 'mdi:solar-power'
+
+ - name: 'PV Generation total'
+ class: 'energy'
+ state_class: 'total_increasing'
+ uom: 'kWh'
+ scale: 0.1
+ rule: 3
+ registers: [0x0687, 0x0686]
+ icon: 'mdi:solar-power'
+
+ - name: 'PV1 Power'
+ class: 'power'
+ state_class: 'measurement'
+ uom: 'W'
+ scale: 10
+ rule: 1
+ registers: [0x0586]
+ icon: 'mdi:solar-power'
+
+ - name: 'PV2 Power'
+ class: 'power'
+ state_class: 'measurement'
+ uom: 'W'
+ scale: 10
+ rule: 1
+ registers: [0x0589]
+ icon: 'mdi:solar-power'
+
+ - name: 'PV1 Voltage'
+ class: 'voltage'
+ state_class: 'measurement'
+ uom: 'V'
+ scale: 0.1
+ rule: 1
+ registers: [0x0584]
+ icon: 'mdi:solar-power'
+
+ - name: 'PV2 Voltage'
+ class: 'voltage'
+ state_class: 'measurement'
+ uom: 'V'
+ scale: 0.1
+ rule: 1
+ registers: [0x0587]
+ icon: 'mdi:solar-power'
+
+ - name: 'PV1 Current'
+ class: 'current'
+ state_class: 'measurement'
+ uom: 'A'
+ scale: 0.01
+ rule: 1
+ registers: [0x0585]
+ icon: 'mdi:solar-power'
+
+ - name: 'PV2 Current'
+ class: 'current'
+ state_class: 'measurement'
+ uom: 'A'
+ scale: 0.01
+ rule: 1
+ registers: [0x0588]
+ icon: 'mdi:solar-power'
+
+ - group: Grid
+ items:
+ - name: 'Grid Frequency'
+ class: 'frequency'
+ state_class: 'measurement'
+ uom: 'Hz'
+ scale: 0.01
+ rule: 1
+ registers: [0x0484]
+ icon: 'mdi:home-lightning-bolt'
+
+ - name: 'Active Power Output Total'
+ class: 'power'
+ state_class: 'measurement'
+ uom: 'W'
+ scale: 10
+ rule: 2
+ registers: [0x0485]
+ icon: 'mdi:home-lightning-bolt'
+
+ - group: Inverter
+ items:
+ - name: 'Inverter status'
+ class: ''
+ state_class: 'measurement'
+ uom: ''
+ scale: 1
+ rule: 1
+ registers: [0x0404]
+ lookup:
+ - key: 0
+ value: 'Stand-by'
+ - key: 1
+ value: 'Self-checking'
+ - key: 2
+ value: 'Normal'
+ - key: 3
+ value: 'FAULT'
+ - key: 4
+ value: 'Permanent'
+ icon: 'mdi:wrench'
+
+ - name: 'Module temperature'
+ class: 'temperature'
+ uom: '°C'
+ scale: 0.1
+ rule: 2
+ registers: [0x0683]
+ icon: 'mdi:thermometer'
+
+ - name: 'Ambient temperature'
+ class: 'temperature'
+ uom: '°C'
+ scale: 1
+ rule: 2
+ registers: [0x0418]
+ icon: 'mdi:thermometer'
+
+ - name: 'Radiator temperature'
+ class: 'temperature'
+ uom: '°C'
+ scale: 1
+ rule: 2
+ registers: [0x041A]
+ icon: 'mdi:thermometer'
+
+ - name: 'Insulation Resistance'
+ class: ''
+ state_class: 'measurement'
+ uom: 'Ω'
+ scale: 1
+ rule: 1
+ registers: [0x042B]
+ icon: 'mdi:omega'
+
+ - group: Alert
+ items:
+ - name: 'Alert'
+ class: ''
+ state_class: ''
+ uom: ''
+ scale: 1
+ rule: 6
+ registers:
+ [
+ 0x0405,
+ 0x0406,
+ 0x0407,
+ 0x0408,
+ 0x0409,
+ 0x040A,
+ 0x040B,
+ 0x040C,
+ 0x040D,
+ 0x040E,
+ 0x040F,
+ 0x0410,
+ ]
+
+ - name: 'Fault 1'
+ class: ''
+ state_class: ''
+ uom: ''
+ scale: 1
+ rule: 1
+ registers: [0x0405]
+ isstr: true
+ icon: 'mdi:wrench'
+ lookup:
+ - key: 0
+ value: 'No error'
+ - key: 1
+ value: 'ID01 Grid Over Voltage Protection'
+ - key: 2
+ value: 'ID02 Grid Under Voltage Protection'
+ - key: 4
+ value: 'ID03 Grid Over Frequency Protection'
+ - key: 8
+ value: 'ID04 Grid Under Frequency Protection'
+ - key: 16
+ value: 'ID05 Leakage current fault'
+ - key: 32
+ value: 'ID06 High penetration error'
+ - key: 64
+ value: 'ID07 Low penetration error'
+ - key: 128
+ value: 'ID08 Islanding error'
+ - key: 256
+ value: 'ID09 Grid voltage transient value overvoltage 1'
+ - key: 512
+ value: 'ID10 Grid voltage transient value overvoltage 2'
+ - key: 1024
+ value: 'ID11 Grid line voltage error'
+ - key: 2048
+ value: 'ID12 Inverter voltage error'
+ - key: 4096
+ value: 'ID13 Anti-backflow overload'
+ - key: 8192
+ value: 'ID14'
+ - key: 16384
+ value: 'ID15'
+ - key: 32768
+ value: 'ID16'
+
+ - name: 'Fault 2'
+ class: ''
+ state_class: ''
+ uom: ''
+ scale: 1
+ rule: 1
+ icon: 'mdi:wrench'
+ isstr: true
+ registers: [0x0406]
+ lookup:
+ - key: 0
+ value: 'No error'
+ - key: 1
+ value: 'ID17 Grid current sampling error'
+ - key: 2
+ value: 'ID18 Grid current DC component sampling error (AC side)'
+ - key: 4
+ value: 'ID19 Grid voltage sampling error (DC side)'
+ - key: 8
+ value: 'ID20 Grid voltage sampling error (AC side)'
+ - key: 16
+ value: 'ID21 Leakage current sampling error (DC side)'
+ - key: 32
+ value: 'ID22 Leakage current sampling error (AC side)'
+ - key: 64
+ value: 'ID23 Load voltage DC component sampling error'
+ - key: 128
+ value: 'ID24 DC input current sampling error'
+ - key: 256
+ value: 'ID25 DC component sampling error of grid current (DC side)'
+ - key: 512
+ value: 'ID26 DC input branch current sampling error'
+ - key: 1024
+ value: 'ID27'
+ - key: 2048
+ value: 'ID28'
+ - key: 4096
+ value: 'ID29 Leakage current consistency error'
+ - key: 8192
+ value: 'ID30 Grid voltage consistency error'
+ - key: 16384
+ value: 'ID31 DCI consistency error'
+ - key: 32768
+ value: 'ID32'
+
+ - name: 'Fault 3'
+ class: ''
+ state_class: ''
+ uom: ''
+ scale: 1
+ rule: 1
+ icon: 'mdi:wrench'
+ isstr: true
+ registers: [0x0407]
+ lookup:
+ - key: 0
+ value: 'No error'
+ - key: 1
+ value: 'ID033 SPI communication error (DC side)'
+ - key: 2
+ value: 'ID034 SPI communication error (AC side)'
+ - key: 4
+ value: 'ID035 Chip error (DC side)'
+ - key: 8
+ value: 'ID036 Chip error (AC side)'
+ - key: 16
+ value: 'ID037 Auxiliary power error'
+ - key: 32
+ value: 'ID038 Inverter soft start failure'
+ - key: 64
+ value: 'ID039 '
+ - key: 128
+ value: 'ID040 '
+ - key: 256
+ value: 'ID041 Relay detection failure'
+ - key: 512
+ value: 'ID042 Low insulation impedance'
+ - key: 1024
+ value: 'ID043 Grounding error'
+ - key: 2048
+ value: 'ID044 Input mode setting error'
+ - key: 4096
+ value: 'ID045 CT error'
+ - key: 8192
+ value: 'ID046 Input reversal error'
+ - key: 16384
+ value: 'ID047 Parallel error'
+ - key: 32768
+ value: 'ID048 Serial number error'
+
+ - name: 'Fault 4'
+ class: ''
+ state_class: ''
+ uom: ''
+ scale: 1
+ rule: 1
+ icon: 'mdi:wrench'
+ registers: [0x0408]
+ isstr: true
+ lookup:
+ - key: 0
+ value: 'No error'
+ - key: 1
+ value: 'ID049 Battery temperature protection'
+ - key: 2
+ value: 'ID050 Heat sink 1 temperature protection'
+ - key: 4
+ value: 'ID051 Heater 2 temperature protection'
+ - key: 8
+ value: 'ID052 Heater 3 temperature protection'
+ - key: 16
+ value: 'ID053 Heatsink 4 temperature protection'
+ - key: 32
+ value: 'ID054 Heatsink 5 temperature protection'
+ - key: 64
+ value: 'ID055 Radiator 6 temperature protection'
+ - key: 128
+ value: 'ID056 '
+ - key: 256
+ value: 'ID057 Ambient temperature 1 protection'
+ - key: 512
+ value: 'ID058 Ambient temperature 2 protection'
+ - key: 1024
+ value: 'ID059 Module 1 temperature protection'
+ - key: 2048
+ value: 'ID060 Module 2 temperature protection'
+ - key: 4096
+ value: 'ID061 Module 3 temperature protection'
+ - key: 8192
+ value: 'ID062 Module temperature difference is too large'
+ - key: 16384
+ value: 'ID063 '
+ - key: 32768
+ value: 'ID064 '
+
+ - name: 'Fault 5'
+ class: ''
+ state_class: ''
+ uom: ''
+ scale: 1
+ rule: 1
+ icon: 'mdi:wrench'
+ registers: [0x0409]
+ isstr: true
+ lookup:
+ - key: 0
+ value: 'No error'
+ - key: 1
+ value: 'ID065 Bus voltage RMS unbalance'
+ - key: 2
+ value: 'ID066 Bus voltage transient value unbalance'
+ - key: 4
+ value: 'ID067 Undervoltage of busbar during grid connection'
+ - key: 8
+ value: 'ID068 Bus bar low voltage'
+ - key: 16
+ value: 'ID069 PV overvoltage'
+ - key: 32
+ value: 'ID070 Battery over-voltage'
+ - key: 64
+ value: 'ID071 LLCBus overvoltage protection'
+ - key: 128
+ value: 'ID072 Inverter bus voltage RMS software overvoltage'
+ - key: 256
+ value: 'ID073 Inverter bus voltage transient value software overvoltage'
+ - key: 512
+ value: 'ID074 Flying Cross Capacitor Overvoltage Protection'
+ - key: 1024
+ value: 'ID075 Flying Cross capacitor undervoltage protection'
+ - key: 2048
+ value: 'ID076 '
+ - key: 4096
+ value: 'ID077 '
+ - key: 8192
+ value: 'ID078 '
+ - key: 16384
+ value: 'ID079 '
+ - key: 32768
+ value: 'ID080 '
+
+ - name: 'Fault 6'
+ class: ''
+ state_class: ''
+ uom: ''
+ scale: 1
+ rule: 1
+ icon: 'mdi:wrench'
+ isstr: true
+ registers: [0x040A]
+ lookup:
+ - key: 0
+ value: 'No error'
+ - key: 1
+ value: 'ID081 Battery overcurrent software protection'
+ - key: 2
+ value: 'ID082 Dci overcurrent protection'
+ - key: 4
+ value: 'ID083 Output transient current protection'
+ - key: 8
+ value: 'ID084 BuckBoost software overcurrent'
+ - key: 16
+ value: 'ID085 Output RMS current protection'
+ - key: 32
+ value: 'ID086 PV instantaneous current overcurrent software protection'
+ - key: 64
+ value: 'ID087 PV parallel uneven current'
+ - key: 128
+ value: 'ID088 Output current unbalance'
+ - key: 256
+ value: 'ID089 PV software overcurrent protection'
+ - key: 512
+ value: 'ID090 Balanced circuit overcurrent protection'
+ - key: 1024
+ value: 'ID091 Resonance protection'
+ - key: 2048
+ value: 'ID092 '
+ - key: 4096
+ value: 'ID093 '
+ - key: 8192
+ value: 'ID094 '
+ - key: 16384
+ value: 'ID095 '
+ - key: 32768
+ value: 'ID096 '
+
+ - name: 'Fault 7'
+ class: ''
+ state_class: ''
+ uom: ''
+ scale: 1
+ rule: 1
+ icon: 'mdi:wrench'
+ isstr: true
+ registers: [0x040B]
+ lookup:
+ - key: 0
+ value: 'No error'
+ - key: 1
+ value: 'ID097 LLC bus hardware overvoltage'
+ - key: 2
+ value: 'ID098 Inverter bus hardware overvoltage'
+ - key: 4
+ value: 'ID099 BuckBoost hardware overcurrent'
+ - key: 8
+ value: 'ID100 Battery hardware overcurrent'
+ - key: 16
+ value: 'ID101 '
+ - key: 32
+ value: 'ID102 PV hardware overcurrent'
+ - key: 64
+ value: 'ID103 AC output hardware overcurrent'
+ - key: 128
+ value: 'ID104 '
+ - key: 256
+ value: 'ID105 Power meter error'
+ - key: 512
+ value: 'ID106 Serial number model error'
+ - key: 1024
+ value: 'ID107 '
+ - key: 2048
+ value: 'ID108 '
+ - key: 4096
+ value: 'ID109 '
+ - key: 8192
+ value: 'ID110 Overload protection 1'
+ - key: 16384
+ value: 'ID111 Overload protection 2'
+ - key: 32768
+ value: 'ID112 Overload protection 3'
+
+ - name: 'Fault 8'
+ class: ''
+ state_class: ''
+ uom: ''
+ scale: 1
+ rule: 1
+ icon: 'mdi:wrench'
+ registers: [0x040C]
+ isstr: true
+ lookup:
+ - key: 0
+ value: 'No error'
+ - key: 1
+ value: 'ID113 Overtemperature derating'
+ - key: 2
+ value: 'ID114 Frequency down load'
+ - key: 4
+ value: 'ID115 Frequency loading'
+ - key: 8
+ value: 'ID116 Voltage down load'
+ - key: 16
+ value: 'ID117 Voltage loading'
+ - key: 32
+ value: 'ID118 '
+ - key: 64
+ value: 'ID119 '
+ - key: 128
+ value: 'ID120 '
+ - key: 256
+ value: 'ID121 Lightning protection failure (DC)'
+ - key: 512
+ value: 'ID122 Lightning protection failure (AC)'
+ - key: 1024
+ value: 'ID123 '
+ - key: 2048
+ value: 'ID124 Battery low voltage protection'
+ - key: 4096
+ value: 'ID125 Battery low voltage shutdown'
+ - key: 8192
+ value: 'ID126 Battery low voltage pre-alarm'
+ - key: 16384
+ value: 'ID127 '
+ - key: 32768
+ value: 'ID128 '
+
+ - name: 'Fault 9'
+ class: ''
+ state_class: ''
+ uom: ''
+ scale: 1
+ rule: 1
+ icon: 'mdi:wrench'
+ isstr: true
+ registers: [0x040D]
+ lookup:
+ - key: 0
+ value: 'No error'
+ - key: 1
+ value: 'ID129 Output hardware overcurrent permanent fault'
+ - key: 2
+ value: 'ID130 Bus overvoltage permanent fault'
+ - key: 4
+ value: 'ID131 Bus hardware over-voltage permanent fault'
+ - key: 8
+ value: 'ID132 PV uneven flow permanent fault'
+ - key: 16
+ value: 'ID133 Battery overcurrent permanent fault in EPS mode'
+ - key: 32
+ value: 'ID134 Output transient overcurrent permanent fault'
+ - key: 64
+ value: 'ID135 Output current unbalance permanent fault'
+ - key: 128
+ value: 'ID136 '
+ - key: 256
+ value: 'ID137 Input mode setting error permanent fault'
+ - key: 512
+ value: 'ID138 Input overcurrent permanent fault'
+ - key: 1024
+ value: 'ID139 Input hardware overcurrent permanent fault'
+ - key: 2048
+ value: 'ID140 Relay permanent fault'
+ - key: 4096
+ value: 'ID141 Bus unbalance permanent fault'
+ - key: 8192
+ value: 'ID142 Lightning protection permanent fault - DC side'
+ - key: 16384
+ value: 'ID143 Lightning protection permanent fault - AC side'
+ - key: 32768
+ value: 'ID144 '
+
+ - name: 'Fault 10'
+ class: ''
+ state_class: ''
+ uom: ''
+ scale: 1
+ rule: 1
+ icon: 'mdi:wrench'
+ isstr: true
+ registers: [0x040E]
+ lookup:
+ - key: 0
+ value: 'No error'
+ - key: 1
+ value: 'ID145 USB fault'
+ - key: 2
+ value: 'ID146 WIFI fault'
+ - key: 4
+ value: 'ID147 Bluetooth fault'
+ - key: 8
+ value: 'ID148 RTC clock fault'
+ - key: 16
+ value: 'ID149 Communication board EEPROM error'
+ - key: 32
+ value: 'ID150 Communication board FLASH error'
+ - key: 64
+ value: 'ID151 '
+ - key: 128
+ value: 'ID152 Safety regulation version error'
+ - key: 256
+ value: 'ID153 SCI communication error (DC side)'
+ - key: 512
+ value: 'ID154 SCI communication error (AC side)'
+ - key: 1024
+ value: 'ID155 SCI communication error (convergence board side)'
+ - key: 2048
+ value: 'ID156 Software version inconsistency'
+ - key: 4096
+ value: 'ID157 Lithium battery 1 communication error'
+ - key: 8192
+ value: 'ID158 Li-ion battery 2 communication error'
+ - key: 16384
+ value: 'ID159 Lithium battery 3 communication error'
+ - key: 32768
+ value: 'ID160 Lithium battery 4 communication failure'
+
+ - name: 'Fault 11'
+ class: ''
+ state_class: ''
+ uom: ''
+ scale: 1
+ rule: 1
+ icon: 'mdi:wrench'
+ registers: [0x040F]
+ isstr: true
+ lookup:
+ - key: 0
+ value: 'No error'
+ - key: 1
+ value: 'ID161 Forced shutdown'
+ - key: 2
+ value: 'ID162 Remote shutdown'
+ - key: 4
+ value: 'ID163 Drms0 shutdown'
+ - key: 8
+ value: 'ID164 '
+ - key: 16
+ value: 'ID165 Remote down load'
+ - key: 32
+ value: 'ID166 Logic interface down load'
+ - key: 64
+ value: 'ID167 Anti-Reverse Flow Downgrade'
+ - key: 128
+ value: 'ID168 '
+ - key: 256
+ value: 'ID169 Fan 1 failure'
+ - key: 512
+ value: 'ID170 Fan 2 failure'
+ - key: 1024
+ value: 'ID171 Fan 3 failure'
+ - key: 2048
+ value: 'ID172 Fan 4 failure'
+ - key: 4096
+ value: 'ID173 Fan 5 failure'
+ - key: 8192
+ value: 'ID174 Fan 6 failure'
+ - key: 16384
+ value: 'ID175 Fan 7 fault'
+ - key: 32768
+ value: 'ID176 Meter communication failure'
+
+ - name: 'Fault 12'
+ class: ''
+ state_class: ''
+ uom: ''
+ scale: 1
+ rule: 1
+ icon: 'mdi:wrench'
+ registers: [0x0410]
+ isstr: true
+ lookup:
+ - key: 0
+ value: 'No error'
+ - key: 1
+ value: 'ID177 BMS over-voltage alarm'
+ - key: 2
+ value: 'ID178 BMS undervoltage alarm'
+ - key: 4
+ value: 'ID179 BMS high temperature alarm'
+ - key: 8
+ value: 'ID180 BMS low temperature alarm'
+ - key: 16
+ value: 'ID181 BMS charge/discharge overload alarm'
+ - key: 32
+ value: 'ID182 BMS short circuit alarm'
+ - key: 64
+ value: 'ID183 BMS version inconsistency'
+ - key: 128
+ value: 'ID184 BMS CAN version inconsistency'
+ - key: 256
+ value: 'ID185 BMS CAN version is too low'
+ - key: 512
+ value: 'ID186 '
+ - key: 1024
+ value: 'ID187 '
+ - key: 2048
+ value: 'ID188 '
+ - key: 4096
+ value: 'ID189 Arc device communication failure'
+ - key: 8192
+ value: 'ID190 DC arc alarm fault'
+ - key: 16384
+ value: 'ID191 PID repair failed'
+ - key: 32768
+ value: 'ID192 PLC module heartbeat loss'
diff --git a/bundles/org.openhab.binding.solarman/src/test/java/org/openhab/binding/solarman/internal/DefinitionParserTest.java b/bundles/org.openhab.binding.solarman/src/test/java/org/openhab/binding/solarman/internal/DefinitionParserTest.java
new file mode 100644
index 00000000000..4d09d2d246d
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/test/java/org/openhab/binding/solarman/internal/DefinitionParserTest.java
@@ -0,0 +1,109 @@
+/**
+ * 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.solarman.internal;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Objects;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.junit.jupiter.api.Test;
+import org.openhab.binding.solarman.internal.defmodel.InverterDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Catalin Sanda - Initial contribution
+ */
+@NonNullByDefault
+public class DefinitionParserTest {
+ private final Logger logger = LoggerFactory.getLogger(DefinitionParserTest.class);
+ private final DefinitionParser definitionParser = new DefinitionParser();
+
+ @Test
+ void testInverterDefinitionsCanBeLoaded() throws IOException {
+ List yamlFiles = scanForYamlFiles("definitions");
+ List definitionIds = extractDefinitionIdFromYamlFiles(yamlFiles);
+
+ assertFalse(definitionIds.isEmpty());
+
+ definitionIds.forEach(definitionId -> {
+ @Nullable
+ InverterDefinition inverterDefinition = definitionParser.parseDefinition(definitionId);
+ assertNotNull(inverterDefinition);
+ });
+ }
+
+ public static List extractDefinitionIdFromYamlFiles(List yamlFiles) {
+ Pattern pattern = Pattern.compile("definitions/(.*)\\.yaml");
+
+ return yamlFiles.stream().map(file -> {
+ Matcher matcher = pattern.matcher(file);
+ return matcher.matches() ? matcher.group(1) : file;
+ }).collect(Collectors.toList());
+ }
+
+ public List scanForYamlFiles(String directoryPath) throws IOException {
+ List yamlFiles = new ArrayList<>();
+ ClassLoader classLoader = Objects.requireNonNull(DefinitionParserTest.class.getClassLoader());
+ Enumeration resources = classLoader.getResources(directoryPath);
+
+ Collections.list(resources).stream().flatMap(resource -> {
+ try {
+ if (resource.getProtocol().equals("jar")) {
+ String path = resource.getPath();
+ String jarPath = path.substring(5, path.indexOf("!"));
+ try (JarFile jarFile = new JarFile(jarPath)) {
+ return jarFile.stream()
+ .filter(e -> e.getName().startsWith(directoryPath) && e.getName().endsWith(".yaml"))
+ .map(JarEntry::getName);
+ }
+ } else if (resource.getProtocol().equals("file")) {
+ return scanDirectory(directoryPath).stream();
+ }
+ } catch (IOException e) {
+ logger.error(e.getMessage(), e);
+ }
+ return Stream.empty();
+ }).forEach(yamlFiles::add);
+
+ return yamlFiles;
+ }
+
+ private static List scanDirectory(String directoryPath) throws IOException {
+ URL url = Objects.requireNonNull(DefinitionParserTest.class.getClassLoader()).getResource(directoryPath);
+ if (url == null) {
+ throw new IllegalArgumentException("Invalid directory path: " + directoryPath);
+ }
+ String[] files = new java.io.File(url.getPath()).list((dir, name) -> name.endsWith(".yaml"));
+ if (files != null) {
+ return Arrays.stream(files).map(file -> directoryPath + "/" + file).toList();
+ }
+ return Collections.emptyList();
+ }
+}
diff --git a/bundles/org.openhab.binding.solarman/src/test/java/org/openhab/binding/solarman/internal/modbus/SolarmanV5ProtocolTest.java b/bundles/org.openhab.binding.solarman/src/test/java/org/openhab/binding/solarman/internal/modbus/SolarmanV5ProtocolTest.java
new file mode 100644
index 00000000000..4577f750533
--- /dev/null
+++ b/bundles/org.openhab.binding.solarman/src/test/java/org/openhab/binding/solarman/internal/modbus/SolarmanV5ProtocolTest.java
@@ -0,0 +1,148 @@
+/**
+ * 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.solarman.internal.modbus;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Map;
+
+import javax.validation.constraints.NotNull;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.openhab.binding.solarman.internal.SolarmanLoggerConfiguration;
+import org.openhab.binding.solarman.internal.modbus.exception.SolarmanException;
+
+/**
+ * @author Catalin Sanda - Initial contribution
+ */
+@ExtendWith(MockitoExtension.class)
+@NonNullByDefault
+class SolarmanV5ProtocolTest {
+ SolarmanLoggerConnection solarmanLoggerConnection = (@NotNull SolarmanLoggerConnection) mock(
+ SolarmanLoggerConnection.class);
+
+ private SolarmanLoggerConfiguration loggerConfiguration = new SolarmanLoggerConfiguration("192.168.1.1", 8899,
+ "1234567890", "sg04lp3", 60, null);
+
+ private SolarmanV5Protocol solarmanV5Protocol = new SolarmanV5Protocol(loggerConfiguration);
+
+ @Test
+ void testbuildSolarmanV5Frame() {
+ byte[] requestFrame = solarmanV5Protocol.buildSolarmanV5Frame((byte) 0x03, 0x0000, 0x0020);
+
+ byte[] expectedFrame = { (byte) 0xA5, (byte) 0x17, (byte) 0x00, (byte) 0x10, (byte) 0x45, (byte) 0x00,
+ (byte) 0x00, (byte) 0xD2, (byte) 0x02, (byte) 0x96, (byte) 0x49, (byte) 0x02, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x03, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x21, (byte) 0x85, (byte) 0xD2, (byte) 0x9D, (byte) 0x15 };
+
+ assertArrayEquals(requestFrame, expectedFrame);
+ }
+
+ @Test
+ void testReadRegister0x01() throws SolarmanException {
+ // given
+ when(solarmanLoggerConnection.sendRequest(any())).thenReturn(
+ hexStringToByteArray("a5000000000000000000000000000000000000000000000000010301000ac84300000015"));
+
+ // when
+ Map regValues = solarmanV5Protocol.readRegisters(solarmanLoggerConnection, (byte) 0x03, 1, 1);
+
+ // then
+ assertEquals(1, regValues.size());
+ assertTrue(regValues.containsKey(1));
+ assertEquals("000A", bytesToHex(regValues.get(1)));
+ }
+
+ @Test
+ void testReadRegisters0x02to0x03() throws SolarmanException {
+ // given
+ when(solarmanLoggerConnection.sendRequest(any())).thenReturn(
+ hexStringToByteArray("a5000000000000000000000000000000000000000000000000010302000a000b13f600000015"));
+
+ // when
+ Map regValues = solarmanV5Protocol.readRegisters(solarmanLoggerConnection, (byte) 0x03, 2, 3);
+
+ // then
+ assertEquals(2, regValues.size());
+ assertTrue(regValues.containsKey(2));
+ assertTrue(regValues.containsKey(3));
+ assertEquals("000A", bytesToHex(regValues.get(2)));
+ assertEquals("000B", bytesToHex(regValues.get(3)));
+ }
+
+ @Test
+ void testReadRegisterSUN10KSG04LP3EUPart1() throws SolarmanException {
+ // given
+ when(solarmanLoggerConnection.sendRequest(any())).thenReturn(hexStringToByteArray(
+ "a53b0010150007482ee38d020121d0060091010000403e486301032800ffffff160a12162420ffffffffffffffffffffffffffffffffffff0001ffff0001ffff000003e81fa45115"));
+
+ // when
+ Map regValues = solarmanV5Protocol.readRegisters(solarmanLoggerConnection, (byte) 0x03, 0x3c,
+ 0x4f);
+
+ // then
+ assertEquals(20, regValues.size());
+ assertTrue(regValues.containsKey(0x3c));
+ assertTrue(regValues.containsKey(0x4f));
+ assertEquals("00FF", bytesToHex(regValues.get(0x3c)));
+ assertEquals("03E8", bytesToHex(regValues.get(0x4f)));
+ }
+
+ @Test
+ void testReadRegisterSUN10KSG04LP3EUPart2() throws SolarmanException {
+ // given
+ when(solarmanLoggerConnection.sendRequest(any())).thenReturn(hexStringToByteArray(
+ "a5330010150008482ee38d020122d0060091010000403e486301032000010000ffffffffffff0001ffffffffffffffffffff0000ffff0011ffffffff3a005715"));
+
+ // when
+ Map regValues = solarmanV5Protocol.readRegisters(solarmanLoggerConnection, (byte) 0x03, 0x50,
+ 0x5f);
+
+ // then
+ assertEquals(16, regValues.size());
+ assertTrue(regValues.containsKey(0x50));
+ assertTrue(regValues.containsKey(0x5f));
+ assertEquals("0001", bytesToHex(regValues.get(0x50)));
+ assertEquals("FFFF", bytesToHex(regValues.get(0x5f)));
+ }
+
+ private static byte[] hexStringToByteArray(String s) {
+ int len = s.length();
+ byte[] data = new byte[len / 2];
+ for (int i = 0; i < len; i += 2) {
+ data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16));
+ }
+ return data;
+ }
+
+ @Nullable
+ private static String bytesToHex(byte @Nullable [] bytes) {
+ if (bytes == null) {
+ return null;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (byte b : bytes) {
+ sb.append(String.format("%02X", b));
+ }
+ return sb.toString();
+ }
+}
diff --git a/bundles/pom.xml b/bundles/pom.xml
index 1f26e09f2c2..2e9c12d782a 100644
--- a/bundles/pom.xml
+++ b/bundles/pom.xml
@@ -379,6 +379,7 @@
org.openhab.binding.solaredge
org.openhab.binding.solarforecast
org.openhab.binding.solarlog
+ org.openhab.binding.solarman
org.openhab.binding.solarmax
org.openhab.binding.solarwatt
org.openhab.binding.solax