mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[solarman] Initial contribution (#16835)
* Initial commit for the Solarman Binding. Signed-off-by: Catalin Sanda <catalin.sanda@gmail.com>
This commit is contained in:
parent
7baec4d651
commit
1cd7a4fd4b
@ -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
|
||||
|
@ -1716,6 +1716,11 @@
|
||||
<artifactId>org.openhab.binding.solarlog</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.solarman</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.solarmax</artifactId>
|
||||
|
13
bundles/org.openhab.binding.solarman/NOTICE
Normal file
13
bundles/org.openhab.binding.solarman/NOTICE
Normal file
@ -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
|
290
bundles/org.openhab.binding.solarman/README.md
Normal file
290
bundles/org.openhab.binding.solarman/README.md
Normal file
@ -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.
|
17
bundles/org.openhab.binding.solarman/pom.xml
Normal file
17
bundles/org.openhab.binding.solarman/pom.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||
<version>4.3.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.solarman</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: Solarman Binding</name>
|
||||
|
||||
</project>
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.solarman-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-binding-solarman" description="Solarman Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.solarman/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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";
|
||||
}
|
@ -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<ThingTypeUID> 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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<Channel> 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<Request> mergedRequests = !additionalRequests.isBlank()
|
||||
? mergeRequests(inverterDefinition.getRequests(), extractAdditionalRequests(additionalRequests))
|
||||
: inverterDefinition.getRequests();
|
||||
|
||||
Map<ParameterItem, ChannelUID> 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<Request> mergedRequests,
|
||||
Map<ParameterItem, ChannelUID> 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 <K, V> Map<K, V> mergeMaps(Map<K, V> map1, Map<K, V> map2) {
|
||||
return Stream.concat(map1.entrySet().stream(), map2.entrySet().stream())
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v1));
|
||||
}
|
||||
|
||||
private Map<ParameterItem, ChannelUID> extractChannelMappingFromChannels(List<Channel> 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<Integer> 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<Request> mergeRequests(List<Request> requestList1, List<Request> requestList2) {
|
||||
return Stream.concat(requestList1.stream(), requestList2.stream()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<Request> 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<ParameterItem, ChannelUID> setupChannelsForInverterDefinition(InverterDefinition inverterDefinition) {
|
||||
ThingBuilder thingBuilder = editThing();
|
||||
|
||||
List<Channel> oldDynamicChannels = thing.getChannels().stream()
|
||||
.filter(channel -> channel.getProperties().containsKey(DYNAMIC_CHANNEL)).toList();
|
||||
|
||||
Map<ParameterItem, Channel> 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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 = "";
|
||||
}
|
@ -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<ParameterItem, Channel> 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<String, Object> configurationMap = objectMapper.convertValue(baseChannelConfig, new TypeReference<>() {
|
||||
});
|
||||
|
||||
configurationMap.forEach(configuration::put);
|
||||
|
||||
return configuration;
|
||||
}
|
||||
|
||||
private String convertRegisters(List<Integer> registers) {
|
||||
return "["
|
||||
+ registers.stream().map(register -> String.format("0x%04X", register)).collect(Collectors.joining(","))
|
||||
+ "]";
|
||||
}
|
||||
}
|
@ -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<Request> requests = new ArrayList<>();
|
||||
private List<Parameter> parameters = new ArrayList<>();
|
||||
|
||||
public String getInverterDefinitionId() {
|
||||
return inverterDefinitionId;
|
||||
}
|
||||
|
||||
public void setInverterDefinitionId(String inverterDefinitionId) {
|
||||
this.inverterDefinitionId = inverterDefinitionId;
|
||||
}
|
||||
|
||||
public List<Request> getRequests() {
|
||||
return requests;
|
||||
}
|
||||
|
||||
public void setRequests(List<Request> requests) {
|
||||
this.requests = requests;
|
||||
}
|
||||
|
||||
public List<Parameter> getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public void setParameters(List<Parameter> parameters) {
|
||||
this.parameters = parameters;
|
||||
}
|
||||
}
|
@ -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<ParameterItem> items = new ArrayList<ParameterItem>();
|
||||
|
||||
public String getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
public void setGroup(String group) {
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
public List<ParameterItem> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
public void setItems(List<ParameterItem> items) {
|
||||
this.items = items;
|
||||
}
|
||||
}
|
@ -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<Integer> 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<Integer> 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<Integer> getRegisters() {
|
||||
return registers;
|
||||
}
|
||||
|
||||
public void setRegisters(List<Integer> 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;
|
||||
}
|
||||
}
|
@ -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) + '}';
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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<Integer, byte[]> 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
|
||||
* <a href="https://pysolarmanv5.readthedocs.io/en/latest/solarmanv5_protocol.html">Solarman V5 Protocol</a>
|
||||
*
|
||||
* @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 <a href="https://www.modbustools.com/modbus.html#function03">Function 03 (03hex) Read Holding
|
||||
* Registers</a>
|
||||
*
|
||||
* @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<Integer, byte[]> parseModbusReadHoldingRegistersResponse(byte @Nullable [] frame, int firstReg,
|
||||
int lastReg) throws SolarmanProtocolException {
|
||||
int regCount = lastReg - firstReg + 1;
|
||||
Map<Integer, byte[]> 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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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)));
|
||||
}
|
||||
}
|
@ -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<ChannelTypeUID, ChannelType> 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<ChannelTypeUID, ChannelType> 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<String> extractInverterDefinitionId(String file) {
|
||||
return Stream.of(file).map(INVERTER_DEFINITION_PATTERN::matcher).filter(Matcher::matches)
|
||||
.map(matcher -> matcher.group(1)).findFirst();
|
||||
}
|
||||
|
||||
public Collection<ChannelType> 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(",")));
|
||||
}
|
||||
}
|
@ -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<Request> requests,
|
||||
SolarmanLoggerConnector solarmanLoggerConnector, SolarmanV5Protocol solarmanV5Protocol,
|
||||
Map<ParameterItem, ChannelUID> 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<ParameterItem, ChannelUID> paramToChannelMapping,
|
||||
Map<Integer, byte[]> readRegistersMap) {
|
||||
paramToChannelMapping.forEach((parameterItem, channelUID) -> {
|
||||
List<Integer> 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<Integer> registers,
|
||||
Map<Integer, byte[]> 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<Integer> registers,
|
||||
Map<Integer, byte[]> 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<Integer> registers,
|
||||
Map<Integer, byte[]> 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<Integer> registers,
|
||||
Map<Integer, byte[]> 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<Integer> registers, Map<Integer, byte[]> 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<Integer> registers,
|
||||
Map<Integer, byte[]> 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<Integer> registers, Map<Integer, byte[]> 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 <T> List<T> reversed(List<T> initialList) {
|
||||
List<T> reversedList = new ArrayList<>(initialList);
|
||||
Collections.reverse(reversedList);
|
||||
return reversedList;
|
||||
}
|
||||
}
|
@ -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<Request, Map<Integer, byte[]>> successfulRequestMap;
|
||||
private final Map<Request, SolarmanException> exceptionRequestMap;
|
||||
|
||||
public SolarmanProcessResult() {
|
||||
this(Collections.emptyMap(), Collections.emptyMap());
|
||||
}
|
||||
|
||||
private SolarmanProcessResult(Map<Request, Map<Integer, byte[]>> successfulRequestMap,
|
||||
Map<Request, SolarmanException> 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<Integer, byte[]> 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<Integer, byte[]> getReadRegistersMap() {
|
||||
return successfulRequestMap.values().stream().reduce(new HashMap<>(), SolarmanProcessResult::mergeMaps);
|
||||
}
|
||||
|
||||
private static <K, V> Map<K, V> mergeMaps(Map<K, V> map1, Map<K, V> 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<Request, SolarmanException> exceptionRequestMap) {
|
||||
return exceptionRequestMap.entrySet().stream().map(entry -> String.format("\tRequest %s returned error: %s\n",
|
||||
entry.getKey().toString(), entry.getValue().getMessage())).reduce("", String::concat);
|
||||
}
|
||||
}
|
@ -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 <A> The type of the first stream elements
|
||||
* @param <B> The type of the second stream elements
|
||||
* @param <C> 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 <A, B, C> Stream<C> 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<A> aIterator = Spliterators.iterator(aSpliterator);
|
||||
Iterator<B> bIterator = Spliterators.iterator(bSpliterator);
|
||||
Iterator<C> cIterator = new Iterator<C>() {
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return aIterator.hasNext() && bIterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public C next() {
|
||||
return zipper.apply(aIterator.next(), bIterator.next());
|
||||
}
|
||||
};
|
||||
|
||||
Spliterator<C> 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 <A> The type of the first object
|
||||
* @param <B> The type of the second object
|
||||
*/
|
||||
public record Tuple<A, B> (A a, B b) {
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<addon:addon id="solarman" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
|
||||
|
||||
<type>binding</type>
|
||||
<name>Solarman Logger Binding</name>
|
||||
<description>This is the binding for Solarman Logger</description>
|
||||
<connection>local</connection>
|
||||
</addon:addon>
|
@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
|
||||
<config-description uri="channel-type-config:solarman:datetime-channel">
|
||||
<parameter name="uom" type="text">
|
||||
<label>Unit of Measurement</label>
|
||||
<description>The unit of measurement used for this channel</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="scale" type="decimal">
|
||||
<label>Scale</label>
|
||||
<description>The scaling factor, the final value will be scaled by this</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="rule" type="integer">
|
||||
<label>Rule</label>
|
||||
<description>The type of measurement. See explanation for possible values</description>
|
||||
<advanced>true</advanced>
|
||||
<options>
|
||||
<option value="1">Unsigned Short</option>
|
||||
<option value="2">Signed Short</option>
|
||||
<option value="3">Unsigned Integer</option>
|
||||
<option value="4">Signed Integer</option>
|
||||
<option value="5">Text</option>
|
||||
<option value="6">Bytes</option>
|
||||
<option value="7">Version</option>
|
||||
<option value="8">Date Time</option>
|
||||
<option value="9">Time</option>
|
||||
</options>
|
||||
</parameter>
|
||||
<parameter name="offset" type="decimal">
|
||||
<label>Offset</label>
|
||||
<description>The offset subtracted from the measurement</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="registers" type="text">
|
||||
<label>Registers</label>
|
||||
<description>Comma separated list of registers to read for the measurement</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</config-description:config-descriptions>
|
@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
|
||||
<config-description uri="channel-type-config:solarman:dynamic-channel">
|
||||
<parameter name="uom" type="text">
|
||||
<label>Unit of Measurement</label>
|
||||
<description>The unit of measurement used for this channel</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="scale" type="decimal">
|
||||
<label>Scale</label>
|
||||
<description>The scaling factor, the final value will be scaled by this</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="rule" type="integer">
|
||||
<label>Rule</label>
|
||||
<description>The type of measurement. See explanation for possible values</description>
|
||||
<advanced>true</advanced>
|
||||
<options>
|
||||
<option value="1">Unsigned Short</option>
|
||||
<option value="2">Signed Short</option>
|
||||
<option value="3">Unsigned Integer</option>
|
||||
<option value="4">Signed Integer</option>
|
||||
<option value="5">Text</option>
|
||||
<option value="6">Bytes</option>
|
||||
<option value="7">Version</option>
|
||||
<option value="8">Date Time</option>
|
||||
<option value="9">Time</option>
|
||||
</options>
|
||||
</parameter>
|
||||
<parameter name="offset" type="decimal">
|
||||
<label>Offset</label>
|
||||
<description>The offset subtracted from the measurement</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="registers" type="text">
|
||||
<label>Registers</label>
|
||||
<description>Comma separated list of registers to read for the measurement</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</config-description:config-descriptions>
|
@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
|
||||
<config-description uri="channel-type-config:solarman:number-channel">
|
||||
<parameter name="uom" type="text">
|
||||
<label>Unit of Measurement</label>
|
||||
<description>The unit of measurement used for this channel</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="scale" type="decimal">
|
||||
<label>Scale</label>
|
||||
<description>The scaling factor, the final value will be scaled by this</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="rule" type="integer">
|
||||
<label>Rule</label>
|
||||
<description>The type of measurement. See explanation for possible values</description>
|
||||
<advanced>true</advanced>
|
||||
<options>
|
||||
<option value="1">Unsigned Short</option>
|
||||
<option value="2">Signed Short</option>
|
||||
<option value="3">Unsigned Integer</option>
|
||||
<option value="4">Signed Integer</option>
|
||||
<option value="5">Text</option>
|
||||
<option value="6">Bytes</option>
|
||||
<option value="7">Version</option>
|
||||
<option value="8">Date Time</option>
|
||||
<option value="9">Time</option>
|
||||
</options>
|
||||
</parameter>
|
||||
<parameter name="offset" type="decimal">
|
||||
<label>Offset</label>
|
||||
<description>The offset subtracted from the measurement</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="registers" type="text">
|
||||
<label>Registers</label>
|
||||
<description>Comma separated list of registers to read for the measurement</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</config-description:config-descriptions>
|
@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
|
||||
<config-description uri="channel-type-config:solarman:string-channel">
|
||||
<parameter name="uom" type="text">
|
||||
<label>Unit of Measurement</label>
|
||||
<description>The unit of measurement used for this channel</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="scale" type="decimal">
|
||||
<label>Scale</label>
|
||||
<description>The scaling factor, the final value will be scaled by this</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="rule" type="integer">
|
||||
<label>Rule</label>
|
||||
<description>The type of measurement. See explanation for possible values</description>
|
||||
<advanced>true</advanced>
|
||||
<options>
|
||||
<option value="1">Unsigned Short</option>
|
||||
<option value="2">Signed Short</option>
|
||||
<option value="3">Unsigned Integer</option>
|
||||
<option value="4">Signed Integer</option>
|
||||
<option value="5">Text</option>
|
||||
<option value="6">Bytes</option>
|
||||
<option value="7">Version</option>
|
||||
<option value="8">Date Time</option>
|
||||
<option value="9">Time</option>
|
||||
</options>
|
||||
</parameter>
|
||||
<parameter name="offset" type="decimal">
|
||||
<label>Offset</label>
|
||||
<description>The offset subtracted from the measurement</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="registers" type="text">
|
||||
<label>Registers</label>
|
||||
<description>Comma separated list of registers to read for the measurement</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</config-description:config-descriptions>
|
@ -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
|
@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="solarman"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<channel-type id="string">
|
||||
<item-type>String</item-type>
|
||||
<label>Text Value</label>
|
||||
<config-description-ref uri="channel-type-config:solarman:string-channel"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="number">
|
||||
<item-type>Number</item-type>
|
||||
<label>Number Value</label>
|
||||
<config-description-ref uri="channel-type-config:solarman:number-channel"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="datetime">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Datetime Value</label>
|
||||
<config-description-ref uri="channel-type-config:solarman:datetime-channel"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="dynamic">
|
||||
<item-type>String</item-type>
|
||||
<label>Dynamic Channel</label>
|
||||
<config-description-ref uri="channel-type-config:solarman:dynamic-channel"/>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="solarman"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<!-- Sample Thing Type -->
|
||||
<thing-type id="logger" extensible="true">
|
||||
<label>Solarman Logger</label>
|
||||
<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.</description>
|
||||
|
||||
<config-description>
|
||||
<parameter name="hostname" type="text" required="true">
|
||||
<context>network-address</context>
|
||||
<label>Hostname</label>
|
||||
<description>Hostname or IP address of the Solarman logger.</description>
|
||||
<advanced>false</advanced>
|
||||
</parameter>
|
||||
<parameter name="port" type="integer" required="false">
|
||||
<label>Port</label>
|
||||
<description>Port of the Solarman logger (default 8899).</description>
|
||||
<default>8899</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="inverterType" type="text" required="true">
|
||||
<label>Inverter Type</label>
|
||||
<description>The type of inverter connected to the logger (default deye_sg04lp3).</description>
|
||||
<advanced>false</advanced>
|
||||
<options>
|
||||
<option value="deye_2mppt">DEYE Microinverter with 2 MPPT Trackers (deye_2mppt)</option>
|
||||
<option value="deye_4mppt">DEYE Microinverter with 4 MPPT Trackers (deye_4mppt)</option>
|
||||
<option value="deye_hybrid">Generic DEYE/Sunsynk/SolArk Hybrid inverters (deye_hybrid)</option>
|
||||
<option value="deye_sg04lp3">DEYE/Sunsynk/SolArk Hybrid 8/12K-SG04LP3 (deye_sg04lp3)</option>
|
||||
<option value="deye_string">Generic DEYE/Sunsynk/SolArk String inverters (deye_string)</option>
|
||||
<option value="kstar_hybrid">KSTAR Hybrid Inverter (kstar_hybrid)</option>
|
||||
<option value="sofar_g3hyd">SOFAR Hybrid Three-Phase Inverter (sofar_g3hyd)</option>
|
||||
<option value="sofar_hyd3k-6k-es">SOFAR Hybrid Single-Phase Inverter (sofar_hyd3k-6k-es)</option>
|
||||
<option value="sofar_lsw3">SOFAR Inverters (sofar_lsw3)</option>
|
||||
<option value="sofar_wifikit">SOFAR WifiKit (sofar_wifikit)</option>
|
||||
<option value="solis_1p8k-5g">SOLIS 1P8K-5G (solis_1p8k-5g)</option>
|
||||
<option value="solis_hybrid">SOLIS Hybrid Inverter (solis_hybrid)</option>
|
||||
<option value="zcs_azzurro-ktl-v3">ZCS Azzurro KTL-V3 Inverters (zcs_azzurro-ktl-v3)</option>
|
||||
</options>
|
||||
</parameter>
|
||||
<parameter name="serialNumber" type="text" required="true">
|
||||
<label>Serial Number</label>
|
||||
<description>Serial number of the Solarman logger.</description>
|
||||
<advanced>false</advanced>
|
||||
</parameter>
|
||||
<parameter name="refreshInterval" type="integer" required="false" unit="s" min="30">
|
||||
<label>Refresh Interval</label>
|
||||
<description>Interval to query the logger (default 60).</description>
|
||||
<default>60</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="additionalRequests" type="text" required="false">
|
||||
<label>Additional Requests</label>
|
||||
<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
|
||||
</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
</thing:thing-descriptions>
|
@ -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
|
@ -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'
|
@ -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]
|
@ -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]
|
@ -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
|
@ -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'
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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'
|
@ -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
|
@ -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'
|
||||
|
@ -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'
|
@ -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'
|
@ -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<String> yamlFiles = scanForYamlFiles("definitions");
|
||||
List<String> definitionIds = extractDefinitionIdFromYamlFiles(yamlFiles);
|
||||
|
||||
assertFalse(definitionIds.isEmpty());
|
||||
|
||||
definitionIds.forEach(definitionId -> {
|
||||
@Nullable
|
||||
InverterDefinition inverterDefinition = definitionParser.parseDefinition(definitionId);
|
||||
assertNotNull(inverterDefinition);
|
||||
});
|
||||
}
|
||||
|
||||
public static List<String> extractDefinitionIdFromYamlFiles(List<String> 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<String> scanForYamlFiles(String directoryPath) throws IOException {
|
||||
List<String> yamlFiles = new ArrayList<>();
|
||||
ClassLoader classLoader = Objects.requireNonNull(DefinitionParserTest.class.getClassLoader());
|
||||
Enumeration<URL> 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<String> 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();
|
||||
}
|
||||
}
|
@ -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<Integer, byte[]> 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<Integer, byte[]> 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<Integer, byte[]> 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<Integer, byte[]> 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();
|
||||
}
|
||||
}
|
@ -379,6 +379,7 @@
|
||||
<module>org.openhab.binding.solaredge</module>
|
||||
<module>org.openhab.binding.solarforecast</module>
|
||||
<module>org.openhab.binding.solarlog</module>
|
||||
<module>org.openhab.binding.solarman</module>
|
||||
<module>org.openhab.binding.solarmax</module>
|
||||
<module>org.openhab.binding.solarwatt</module>
|
||||
<module>org.openhab.binding.solax</module>
|
||||
|
Loading…
Reference in New Issue
Block a user