mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 07:02:02 +01:00
[sungrow] Initial contribution (#15130)
* 0000: Implementation --------- Signed-off-by: Sönke Küper <soenkekueper@gmx.de> Signed-off-by: Leo Siepel <leosiepel@gmail.com> Co-authored-by: Wouter Born <github@maindrain.net> Co-authored-by: Eric Bodden <eric.bodden@upb.de> Co-authored-by: Leo Siepel <leosiepel@gmail.com>
This commit is contained in:
parent
c5a3753dd2
commit
31e88692f9
@ -219,6 +219,7 @@
|
||||
/bundles/org.openhab.binding.modbus.sbc/ @fwolter
|
||||
/bundles/org.openhab.binding.modbus.stiebeleltron/ @pail23
|
||||
/bundles/org.openhab.binding.modbus.studer/ @giovannimirulla
|
||||
/bundles/org.openhab.binding.modbus.sungrow/ @soenkekueper
|
||||
/bundles/org.openhab.binding.modbus.sunspec/ @mrbig
|
||||
/bundles/org.openhab.binding.monopriceaudio/ @mlobstein
|
||||
/bundles/org.openhab.binding.mpd/ @stefanroellin
|
||||
|
@ -1086,6 +1086,11 @@
|
||||
<artifactId>org.openhab.binding.modbus.studer</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.modbus.sungrow</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.modbus.sunspec</artifactId>
|
||||
|
13
bundles/org.openhab.binding.modbus.sungrow/NOTICE
Normal file
13
bundles/org.openhab.binding.modbus.sungrow/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
|
174
bundles/org.openhab.binding.modbus.sungrow/README.md
Normal file
174
bundles/org.openhab.binding.modbus.sungrow/README.md
Normal file
@ -0,0 +1,174 @@
|
||||
# Modbus Sungrow Binding
|
||||
|
||||
This binding integrates the sungrow inverters into openHAB.
|
||||
It is based on the Sungrow specification "Communication Protocol of Residential Hybrid Inverter V1.0.23", which can be found here: https://github.com/bohdan-s/SunGather/issues/36.
|
||||
|
||||
## Supported Inverters
|
||||
|
||||
As defined within the spec mentioned above the following inverters are supported, but not all are tested yet:
|
||||
|
||||
- SH3K6
|
||||
- SH4K6
|
||||
- SH5K-20
|
||||
- SH5K-V13
|
||||
- SH3K6-30
|
||||
- SH4K6-30
|
||||
- SH5K-30
|
||||
- SH3.0RS
|
||||
- SH3.6RS
|
||||
- SH4.0RS
|
||||
- SH5.0RS
|
||||
- SH6.0RS
|
||||
- SH5.0RT
|
||||
- SH6.0RT
|
||||
- SH8.0RT
|
||||
- SH10RT
|
||||
|
||||
## Supported Things
|
||||
|
||||
The binding supports only one thing:
|
||||
|
||||
- `sungrow-inverter`: The sungrow inverter
|
||||
|
||||
## Preparation
|
||||
|
||||
The data from the inverter is read via Modbus. So you need to configure a Modbus Serial Slave `serial` or Modbus TCP Slave `tcp` as bridge first.
|
||||
If you are using a Modbus TCP Slave and the WiNet-S Communication Module please ensure:
|
||||
|
||||
- that you have the correct IP-Address of your WiNet-S Device
|
||||
- that Modbus is enabled within the Communication Module
|
||||
- that you've the correct port number
|
||||
- that the white list is disabled or your openHAB instance IP is listed
|
||||
|
||||
Enabling modbus and whitelist setting can be done in WiNet-S Web-UI as shown below:
|
||||
<img src="./doc/WiNet-S_Modbus.PNG" alt="WiNet-S Modbus configuration"/>
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
Once you've configured the Modbus TCP Slave or Modbus Serial Slave as Bridge you can configure the Sungrow inverter thing.
|
||||
You just have to select the configured bridge and optional configure the polling interval.
|
||||
|
||||
### Sungrow Inverter (`sungrow-inverter`)
|
||||
|
||||
| Name | Type | Description | Default | Required | Advanced |
|
||||
|--------------|---------|--------------------------------------|---------|----------|----------|
|
||||
| pollInterval | integer | Interval the device is polled in ms. | 5000 | yes | no |
|
||||
|
||||
## Channels
|
||||
|
||||
The `sungrow-inverter` thing has channels that serve the current state of the sungrow inverter, as you are used to from the iSolareCloud Website and App.
|
||||
|
||||
| Channel Type ID | Item Type | Description | Advanced | Channel Group |
|
||||
|------------------------------------|--------------------------|---------------------------------------|-----------|---------------------|
|
||||
| sg-internal-temperature | Number:Temperature | Internal Temperature | yes | Overview |
|
||||
| sg-total-dc-power | Number:Power | Total DC Power | no | Overview |
|
||||
| sg-phase-a-voltage | Number:ElectricPotential | Phase A Voltage | yes | Overview |
|
||||
| sg-phase-b-voltage | Number:ElectricPotential | Phase B Voltage | yes | Overview |
|
||||
| sg-phase-c-voltage | Number:ElectricPotential | Phase C Voltage | yes | Overview |
|
||||
| sg-daily-pv-generation | Number:Energy | Daily PV Generation | no | Overview |
|
||||
| sg-total-pv-generation | Number:Energy | Total PV Generation | no | Overview |
|
||||
| sg-reactive-power | Number:Power | Reactive Power | yes | Overview |
|
||||
| sg-power-factor | Number:Dimensionless | Power Factor | yes | Overview |
|
||||
| sg-phase-a-current | Number:ElectricCurrent | Phase A Current | yes | Overview |
|
||||
| sg-phase-b-current | Number:ElectricCurrent | Phase B Current | yes | Overview |
|
||||
| sg-phase-c-current | Number:ElectricCurrent | Phase C Current | yes | Overview |
|
||||
| sg-total-active-power | Number:Power | Total Active Power | no | Overview |
|
||||
| sg-grid-frequency | Number:Frequency | Grid Frequency | yes | Overview |
|
||||
| sg-mppt1-voltage | Number:ElectricPotential | MPPT1 Voltage | yes | MPPT Information |
|
||||
| sg-mppt1-current | Number:ElectricCurrent | MPPT1 Current | yes | MPPT Information |
|
||||
| sg-mppt2-voltage | Number:ElectricPotential | MPPT2 Voltage | yes | MPPT Information |
|
||||
| sg-mppt2-current | Number:ElectricCurrent | MPPT2 Current | yes | MPPT Information |
|
||||
| sg-daily-battery-charge | Number:Energy | Daily Battery Charge | no | Battery Information |
|
||||
| sg-total-battery-charge | Number:Energy | Total Battery Charge | no | Battery Information |
|
||||
| sg-battery-voltage | Number:ElectricPotential | Battery Voltage | yes | Battery Information |
|
||||
| sg-battery-current | Number:ElectricCurrent | Battery Current | yes | Battery Information |
|
||||
| sg-battery-power | Number:Power | Battery Power | no | Battery Information |
|
||||
| sg-battery-level | Number:Dimensionless | Battery Level | no | Battery Information |
|
||||
| sg-battery-healthy | Number:Dimensionless | Battery Healthy | no | Battery Information |
|
||||
| sg-battery-temperature | Number:Temperature | Battery Temperature | no | Battery Information |
|
||||
| sg-daily-battery-discharge-energy | Number:Energy | Daily Battery Discharge Energy | no | Battery Information |
|
||||
| sg-total-battery-discharge-energy | Number:Energy | Total Battery Discharge Energy | no | Battery Information |
|
||||
| sg-battery-capacity | Number:Energy | Battery Capacity | no | Battery Information |
|
||||
| sg-daily-charge-energy | Number:Energy | Daily Charge Energy | no | Battery Information |
|
||||
| sg-total-charge-energy | Number:Energy | Total Charge Energy | no | Battery Information |
|
||||
| sg-daily-import-energy | Number:Energy | Daily Import Energy | no | Grid Information |
|
||||
| sg-total-import-energy | Number:Energy | Total Import Energy | no | Grid Information |
|
||||
| sg-daily-export-energy | Number:Energy | Daily Export Energy | no | Grid Information |
|
||||
| sg-total-export-energy | Number:Energy | Total Export Energy | no | Grid Information |
|
||||
| sg-daily-export-power-from-pv | Number:Power | Daily Export Power from PV | no | Grid Information |
|
||||
| sg-total-export-energy-from-pv | Number:Energy | Total Export Energy from PV | no | Grid Information |
|
||||
| sg-export-power | Number:Power | Export Power | no | Grid Information |
|
||||
| sg-load-power | Number:Power | Load Power | no | Load Information |
|
||||
| sg-daily-direct-energy-consumption | Number:Energy | Daily Direct Energy Consumption | no | Load Information |
|
||||
| sg-total-direct-energy-consumption | Number:Energy | Total Direct Energy Consumption | no | Load Information |
|
||||
| sg-self-consumption-today | Number:Dimensionless | Self Consumption Today | no | Load Information |
|
||||
|
||||
## Full Example
|
||||
|
||||
This example shows how to configure a sungrow inverter connected via modbus and uses the most common channels.
|
||||
|
||||
_sungrow.things_
|
||||
|
||||
```java
|
||||
Bridge modbus:tcp:sungrowBridge [ host="10.0.0.2", port=502, id=1, enableDiscovery=false ] {
|
||||
Thing sungrow-inverter sungrowInverter "Sungrow Inverter" [ pollInterval=5000 ]
|
||||
}
|
||||
```
|
||||
|
||||
_sungrow.items_
|
||||
|
||||
```java
|
||||
// Groups
|
||||
Group sungrowInverter "Sungrow Inverter" ["Inverter"]
|
||||
Group overview "Overview" (sungrowInverter)
|
||||
Group batteryInformation "Battery information" (sungrowInverter)
|
||||
Group gridInformation "Grid information" (sungrowInverter)
|
||||
Group loadInformation "Load information" (sungrowInverter)
|
||||
|
||||
// Overview
|
||||
Number:Power total_active_power "Total Active Power" (overview) ["Measurement", "Power"] {channel="modbus:sungrow-inverter:sungrowBridge:sungrowInverter:sg-overview#sg-total-active-power"}
|
||||
Number:Power total_dc_power "Total DC Power" (overview) ["Measurement", "Power"] {channel="modbus:sungrow-inverter:sungrowBridge:sungrowInverter:sg-overview#sg-total-dc-power"}
|
||||
Number:Energy daily_pv_generation "Daily PV Generation" (overview) ["Measurement", "Energy"] {channel="modbus:sungrow-inverter:sungrowBridge:sungrowInverter:sg-overview#sg-daily-pv-generation"}
|
||||
Number:Energy total_pv_generation "Total PV Generation" (overview) ["Measurement", "Energy"] {channel="modbus:sungrow-inverter:sungrowBridge:sungrowInverter:sg-overview#sg-total-pv-generation"}
|
||||
|
||||
// Battery information
|
||||
Number:Power battery_power "Battery Power" (batteryInformation) ["Measurement", "Power"] {channel="modbus:sungrow-inverter:sungrowBridge:sungrowInverter:sg-battery-information#sg-battery-power"}
|
||||
Number:Dimensionless battery_level "Battery Level" (batteryInformation) ["Measurement", "Energy"] {channel="modbus:sungrow-inverter:sungrowBridge:sungrowInverter:sg-battery-information#sg-battery-level"}
|
||||
Number:Energy daily_charge_energy "Daily Battery Charge Energy" (batteryInformation) ["Measurement", "Energy"] {channel="modbus:sungrow-inverter:sungrowBridge:sungrowInverter:sg-battery-information#sg-daily-charge-energy"}
|
||||
Number:Energy daily_discharge_energy "Daily Battery Discharge Energy" (batteryInformation) ["Measurement", "Energy"] {channel="modbus:sungrow-inverter:sungrowBridge:sungrowInverter:sg-battery-information#sg-daily-battery-discharge-energy"}
|
||||
|
||||
// Grid information
|
||||
Number:Power export_power "Export Power" (gridInformation) ["Measurement", "Power"] {channel="modbus:sungrow-inverter:sungrowBridge:sungrowInverter:sg-grid-information#sg-export-power"}
|
||||
Number:Energy daily_export_energy "Daily Export Energy" (gridInformation) ["Measurement", "Energy"] {channel="modbus:sungrow-inverter:sungrowBridge:sungrowInverter:sg-grid-information#sg-daily-export-energy"}
|
||||
Number:Energy daily_import_energy "Daily Import Energy" (gridInformation) ["Measurement", "Energy"] {channel="modbus:sungrow-inverter:sungrowBridge:sungrowInverter:sg-grid-information#sg-daily-import-energy"}
|
||||
|
||||
// Load information
|
||||
Number:Power load_power "Load Power" (loadInformation) ["Measurement", "Power"] {channel="modbus:sungrow-inverter:sungrowBridge:sungrowInverter:sg-load-information#sg-load-power"}
|
||||
Number:Energy daily_direct_energy_consumption "Daily Direct Energy Consumption" (loadInformation) ["Measurement", "Energy"] {channel="modbus:sungrow-inverter:sungrowBridge:sungrowInverter:sg-load-information#sg-daily-direct-energy-consumption"}
|
||||
```
|
||||
|
||||
_sungrow.sitemap_
|
||||
|
||||
```perl
|
||||
sitemap sungrow label="Sungrow Binding"
|
||||
{
|
||||
Frame {
|
||||
Text item=total_active_power
|
||||
Text item=total_dc_power
|
||||
Text item=daily_pv_generation
|
||||
Text item=total_pv_generation
|
||||
|
||||
Text item=battery_power
|
||||
Text item=battery_level
|
||||
Text item=daily_charge_energy
|
||||
Text item=daily_discharge_energy
|
||||
|
||||
Text item=export_power
|
||||
Text item=daily_export_energy
|
||||
Text item=daily_import_energy
|
||||
|
||||
Text item=load_power
|
||||
Text item=daily_direct_energy_consumption
|
||||
}
|
||||
}
|
||||
```
|
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
27
bundles/org.openhab.binding.modbus.sungrow/pom.xml
Normal file
27
bundles/org.openhab.binding.modbus.sungrow/pom.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
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.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.modbus.sungrow</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: Modbus Sungrow Binding</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.modbus</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.modbus.sungrow.internal;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Constants for converting values.
|
||||
*
|
||||
* @author Sönke Küper - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
final class ConversionConstants {
|
||||
|
||||
private ConversionConstants() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplicand for 0.1.
|
||||
*/
|
||||
static final BigDecimal DIV_BY_TEN = new BigDecimal(BigInteger.ONE, 1);
|
||||
|
||||
/**
|
||||
* Value conversion from Celsius to Kelvin.
|
||||
*/
|
||||
static final Function<BigDecimal, BigDecimal> CELSIUS_TO_KELVIN = (BigDecimal celsius) -> celsius
|
||||
.add(new BigDecimal(273.15f));
|
||||
}
|
@ -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.modbus.sungrow.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.modbus.ModbusBindingConstants;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link ModbusSungrowBindingConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Sönke Küper - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ModbusSungrowBindingConstants {
|
||||
|
||||
/**
|
||||
* ThingType-ID for Inverter.
|
||||
*/
|
||||
public static final ThingTypeUID THING_TYPE_INVERTER = new ThingTypeUID(ModbusBindingConstants.BINDING_ID,
|
||||
"sungrow-inverter");
|
||||
}
|
@ -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.modbus.sungrow.internal;
|
||||
|
||||
import static org.openhab.binding.modbus.sungrow.internal.ModbusSungrowBindingConstants.*;
|
||||
|
||||
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 ModbusSungrowHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Sönke Küper - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(configurationPid = "binding.sungrow", service = ThingHandlerFactory.class)
|
||||
public class ModbusSungrowHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_INVERTER);
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (THING_TYPE_INVERTER.equals(thingTypeUID)) {
|
||||
return new SungrowInverterHandler(thing);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 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.modbus.sungrow.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link SungrowInverterConfiguration} class contains fields mapping thing configuration parameters.
|
||||
*
|
||||
* @author Sönke Küper - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SungrowInverterConfiguration {
|
||||
|
||||
public int pollInterval;
|
||||
}
|
@ -0,0 +1,185 @@
|
||||
/**
|
||||
* 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.modbus.sungrow.internal;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.modbus.handler.BaseModbusThingHandler;
|
||||
import org.openhab.core.io.transport.modbus.AsyncModbusFailure;
|
||||
import org.openhab.core.io.transport.modbus.AsyncModbusReadResult;
|
||||
import org.openhab.core.io.transport.modbus.ModbusBitUtilities;
|
||||
import org.openhab.core.io.transport.modbus.ModbusConstants;
|
||||
import org.openhab.core.io.transport.modbus.ModbusReadFunctionCode;
|
||||
import org.openhab.core.io.transport.modbus.ModbusReadRequestBlueprint;
|
||||
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.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link SungrowInverterHandler} is responsible for reading the modbus values of the
|
||||
* sungrow inverter.
|
||||
*
|
||||
* @author Sönke Küper - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SungrowInverterHandler extends BaseModbusThingHandler {
|
||||
|
||||
@NonNullByDefault
|
||||
private static final class ModbusRequest {
|
||||
|
||||
private final Deque<SungrowInverterRegisters> registers;
|
||||
private final ModbusReadRequestBlueprint blueprint;
|
||||
|
||||
public ModbusRequest(Deque<SungrowInverterRegisters> registers, int slaveId) {
|
||||
this.registers = registers;
|
||||
this.blueprint = initReadRequest(registers, slaveId);
|
||||
}
|
||||
|
||||
private ModbusReadRequestBlueprint initReadRequest(Deque<SungrowInverterRegisters> registers, int slaveId) {
|
||||
int firstRegister = registers.getFirst().getRegisterNumber();
|
||||
int lastRegister = registers.getLast().getRegisterNumber();
|
||||
int length = lastRegister - firstRegister + registers.getLast().getRegisterCount();
|
||||
assert length <= ModbusConstants.MAX_REGISTERS_READ_COUNT;
|
||||
|
||||
return new ModbusReadRequestBlueprint( //
|
||||
slaveId, //
|
||||
ModbusReadFunctionCode.READ_INPUT_REGISTERS, //
|
||||
firstRegister - 1, //
|
||||
length, //
|
||||
TRIES //
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(SungrowInverterHandler.class);
|
||||
|
||||
private static final int TRIES = 1;
|
||||
private List<ModbusRequest> modbusRequests = new ArrayList<>();
|
||||
|
||||
public SungrowInverterHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits the SungrowInverterRegisters into multiple ModbusRequest, to ensure the max request size.
|
||||
*/
|
||||
private List<ModbusRequest> buildRequests() {
|
||||
final List<ModbusRequest> requests = new ArrayList<>();
|
||||
Deque<SungrowInverterRegisters> currentRequest = new ArrayDeque<>();
|
||||
int currentRequestFirstRegister = 0;
|
||||
|
||||
for (SungrowInverterRegisters channel : SungrowInverterRegisters.values()) {
|
||||
|
||||
if (currentRequest.isEmpty()) {
|
||||
currentRequest.add(channel);
|
||||
currentRequestFirstRegister = channel.getRegisterNumber();
|
||||
} else {
|
||||
int sizeWithRegisterAdded = channel.getRegisterNumber() - currentRequestFirstRegister
|
||||
+ channel.getRegisterCount();
|
||||
if (sizeWithRegisterAdded > ModbusConstants.MAX_REGISTERS_READ_COUNT) {
|
||||
requests.add(new ModbusRequest(currentRequest, getSlaveId()));
|
||||
currentRequest = new ArrayDeque<>();
|
||||
|
||||
currentRequest.add(channel);
|
||||
currentRequestFirstRegister = channel.getRegisterNumber();
|
||||
} else {
|
||||
currentRequest.add(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!currentRequest.isEmpty()) {
|
||||
requests.add(new ModbusRequest(currentRequest, getSlaveId()));
|
||||
}
|
||||
logger.debug("Created {} modbus request templates.", requests.size());
|
||||
return requests;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (command instanceof RefreshType && !this.modbusRequests.isEmpty()) {
|
||||
for (ModbusRequest request : this.modbusRequests) {
|
||||
submitOneTimePoll( //
|
||||
request.blueprint, //
|
||||
(AsyncModbusReadResult result) -> this.readSuccessful(request, result), //
|
||||
this::readError //
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modbusInitialize() {
|
||||
final SungrowInverterConfiguration config = getConfigAs(SungrowInverterConfiguration.class);
|
||||
|
||||
if (config.pollInterval <= 0) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Invalid poll interval: " + config.pollInterval);
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateStatus(ThingStatus.UNKNOWN);
|
||||
|
||||
this.modbusRequests = this.buildRequests();
|
||||
|
||||
for (ModbusRequest request : modbusRequests) {
|
||||
registerRegularPoll( //
|
||||
request.blueprint, //
|
||||
config.pollInterval, //
|
||||
0, //
|
||||
(AsyncModbusReadResult result) -> this.readSuccessful(request, result), //
|
||||
this::readError //
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void readSuccessful(ModbusRequest request, AsyncModbusReadResult result) {
|
||||
result.getRegisters().ifPresent(registers -> {
|
||||
if (getThing().getStatus() != ThingStatus.ONLINE) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
|
||||
int firstRegister = request.registers.getFirst().getRegisterNumber();
|
||||
|
||||
for (SungrowInverterRegisters channel : request.registers) {
|
||||
int index = channel.getRegisterNumber() - firstRegister;
|
||||
|
||||
ModbusBitUtilities.extractStateFromRegisters(registers, index, channel.getType())
|
||||
.map(channel::createState).ifPresent(v -> updateState(createChannelUid(channel), v));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void readError(AsyncModbusFailure<ModbusReadRequestBlueprint> error) {
|
||||
this.logger.debug("Failed to get modbus data", error.getCause());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Failed to retrieve data: " + error.getCause().getMessage());
|
||||
}
|
||||
|
||||
private ChannelUID createChannelUid(SungrowInverterRegisters register) {
|
||||
return new ChannelUID( //
|
||||
thing.getUID(), //
|
||||
"sg-" + register.getChannelGroup(), //
|
||||
"sg-" + register.getChannelName() //
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,255 @@
|
||||
/**
|
||||
* 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.modbus.sungrow.internal;
|
||||
|
||||
import static org.openhab.core.io.transport.modbus.ModbusConstants.ValueType.INT16;
|
||||
import static org.openhab.core.io.transport.modbus.ModbusConstants.ValueType.INT32_SWAP;
|
||||
import static org.openhab.core.io.transport.modbus.ModbusConstants.ValueType.UINT16;
|
||||
import static org.openhab.core.io.transport.modbus.ModbusConstants.ValueType.UINT32_SWAP;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.measure.Unit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.io.transport.modbus.ModbusConstants.ValueType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.types.State;
|
||||
|
||||
/**
|
||||
* The {@link SungrowInverterRegisters} is responsible for defining Modbus registers and their units.
|
||||
*
|
||||
* @author Sönke Küper - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public enum SungrowInverterRegisters {
|
||||
|
||||
// the following register numbers are 1-based. They need to be converted before sending them on the wire.
|
||||
// Registers are duplicate to DAILY_PV_GENERATION / TOTAL_PV_GENERATION
|
||||
// DAILY_OUTPUT_ENERGY(5003, UINT16, ConversionConstants.DIV_BY_TEN, quantityTypeFactory(Units.KILOWATT_HOUR),
|
||||
// TOTAL_OUTPUT_ENERGY(5004, UINT32_SWAP, ConversionConstants.DIV_BY_TEN, quantityTypeFactory(Units.KILOWATT_HOUR),
|
||||
|
||||
INTERNAL_TEMPERATURE(5008, INT16, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.KELVIN),
|
||||
ConversionConstants.CELSIUS_TO_KELVIN, "overview"),
|
||||
MPPT1_VOLTAGE(5011, UINT16, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.VOLT), "mppt-information"),
|
||||
MPPT1_CURRENT(5012, UINT16, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.AMPERE), "mppt-information"),
|
||||
MPPT2_VOLTAGE(5013, UINT16, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.VOLT), "mppt-information"),
|
||||
MPPT2_CURRENT(5014, UINT16, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.AMPERE), "mppt-information"),
|
||||
TOTAL_DC_POWER(5017, UINT32_SWAP, BigDecimal.ONE, quantityFactory(Units.WATT), "overview"),
|
||||
PHASE_A_VOLTAGE(5019, UINT16, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.VOLT), "overview"),
|
||||
PHASE_B_VOLTAGE(5020, UINT16, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.VOLT), "overview"),
|
||||
PHASE_C_VOLTAGE(5021, UINT16, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.VOLT), "overview"),
|
||||
|
||||
REACTIVE_POWER(5033, INT32_SWAP, BigDecimal.ONE, quantityFactory(Units.VAR), "overview"),
|
||||
POWER_FACTOR(5035, INT16, new BigDecimal(BigInteger.ONE, 3), DecimalType::new, "overview"),
|
||||
GRID_FREQUENCY(5036, UINT16, new BigDecimal(BigInteger.ONE, 2), quantityFactory(Units.HERTZ), "overview"),
|
||||
|
||||
/*
|
||||
* Not working
|
||||
* EXPORT_LIMIT_MIN(10, 5622, UINT16, quantityTypeFactory(Units.WATT)),
|
||||
* EXPORT_LIMIT_MAX(10, 5623, UINT16, quantityTypeFactory(Units.WATT)),
|
||||
* BDC_RATED_POWER(100, 5628, UINT16, quantityTypeFactory(Units.WATT)),
|
||||
* MAX_CHARGING_CURRENT(1, 5635, UINT16, quantityTypeFactory(Units.AMPERE)),
|
||||
* MAX_DISCHARGING_CURRENT(1, 5636, UINT16, quantityTypeFactory(Units.AMPERE)),
|
||||
* PV_POWER_TODAY(1, 6100, UINT16, quantityTypeFactory(Units.WATT)),
|
||||
* DAILY_PV_ENERGY_YIELDS(1, 6196, UINT16, quantityTypeFactory(Units.KILOWATT_HOUR)),
|
||||
* MONTHLY_PV_ENERGY_YIELDS(1, 9227, UINT16, quantityTypeFactory(Units.KILOWATT_HOUR)),
|
||||
*/
|
||||
|
||||
/**
|
||||
* Registers return invalid values.
|
||||
* SYSTEM_STATE(13000, UINT16, 1, quantityTypeFactory(Units.ONE)),
|
||||
* RUNNING_STATE(13001, UINT16, 1, quantityTypeFactory(Units.ONE)),
|
||||
*/
|
||||
|
||||
DAILY_PV_GENERATION(13002, UINT16, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.KILOWATT_HOUR),
|
||||
"overview"),
|
||||
TOTAL_PV_GENERATION(13003, UINT32_SWAP, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.KILOWATT_HOUR),
|
||||
"overview"),
|
||||
DAILY_EXPORT_ENERGY_FROM_PV(13005, UINT16, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.KILOWATT_HOUR),
|
||||
"grid-information"),
|
||||
TOTAL_EXPORT_ENERGY_FROM_PV(13006, UINT32_SWAP, ConversionConstants.DIV_BY_TEN,
|
||||
quantityFactory(Units.KILOWATT_HOUR), "grid-information"),
|
||||
LOAD_POWER(13008, INT32_SWAP, BigDecimal.ONE, quantityFactory(Units.WATT), "load-information"),
|
||||
EXPORT_POWER(13010, INT32_SWAP, BigDecimal.ONE, quantityFactory(Units.WATT), "grid-information"),
|
||||
DAILY_BATTERY_CHARGE(13012, UINT16, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.KILOWATT_HOUR),
|
||||
"battery-information"),
|
||||
TOTAL_BATTERY_CHARGE(13013, UINT32_SWAP, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.KILOWATT_HOUR),
|
||||
"battery-information"),
|
||||
/*
|
||||
* Not working
|
||||
* CO2_REDUCTION(13015, UINT32_SWAP, ConversionConstants.DIV_BY_TEN, tech.units.indriya.unit.Units.KILOGRAM),
|
||||
*/
|
||||
DAILY_DIRECT_ENERGY_CONSUMPTION(13017, UINT16, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.KILOWATT_HOUR),
|
||||
"load-information"),
|
||||
TOTAL_DIRECT_ENERGY_CONSUMPTION(13018, UINT32_SWAP, ConversionConstants.DIV_BY_TEN,
|
||||
quantityFactory(Units.KILOWATT_HOUR), "load-information"),
|
||||
BATTERY_VOLTAGE(13020, UINT16, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.VOLT), "battery-information"),
|
||||
BATTERY_CURRENT(13021, UINT16, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.AMPERE),
|
||||
"battery-information"),
|
||||
BATTERY_POWER(13022, UINT16, BigDecimal.ONE, quantityFactory(Units.WATT), "battery-information"),
|
||||
BATTERY_LEVEL(13023, UINT16, ConversionConstants.DIV_BY_TEN, PercentType::new, "battery-information"),
|
||||
BATTERY_HEALTHY(13024, UINT16, ConversionConstants.DIV_BY_TEN, PercentType::new, "battery-information"),
|
||||
BATTERY_TEMPERATURE(13025, INT16, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.KELVIN),
|
||||
ConversionConstants.CELSIUS_TO_KELVIN, "battery-information"),
|
||||
DAILY_BATTERY_DISCHARGE_ENERGY(13026, UINT16, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.KILOWATT_HOUR),
|
||||
"battery-information"),
|
||||
TOTAL_BATTERY_DISCHARGE_ENERGY(13027, UINT32_SWAP, ConversionConstants.DIV_BY_TEN,
|
||||
quantityFactory(Units.KILOWATT_HOUR), "battery-information"),
|
||||
SELF_CONSUMPTION_TODAY(13029, UINT16, ConversionConstants.DIV_BY_TEN, PercentType::new, "load-information"),
|
||||
// Not working
|
||||
// GRID_STATE(13030, UINT16, 1, quantityTypeFactory(Units.ONE, "grid-information"),
|
||||
PHASE_A_CURRENT(13031, INT16, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.AMPERE), "overview"),
|
||||
PHASE_B_CURRENT(13032, INT16, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.AMPERE), "overview"),
|
||||
PHASE_C_CURRENT(13033, INT16, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.AMPERE), "overview"),
|
||||
TOTAL_ACTIVE_POWER(13034, INT32_SWAP, BigDecimal.ONE, quantityFactory(Units.WATT), "overview"),
|
||||
DAILY_IMPORT_ENERGY(13036, UINT16, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.KILOWATT_HOUR),
|
||||
"grid-information"),
|
||||
TOTAL_IMPORT_ENERGY(13037, UINT32_SWAP, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.KILOWATT_HOUR),
|
||||
"grid-information"),
|
||||
BATTERY_CAPACITY(13039, UINT16, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.KILOWATT_HOUR),
|
||||
"battery-information"),
|
||||
DAILY_CHARGE_ENERGY(13040, UINT16, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.KILOWATT_HOUR),
|
||||
"battery-information"),
|
||||
TOTAL_CHARGE_ENERGY(13041, UINT32_SWAP, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.KILOWATT_HOUR),
|
||||
"battery-information"),
|
||||
// DRM_STATE(13043, UINT16, 1, quantityTypeFactory(Units.ONE, "channelGroup"),
|
||||
|
||||
DAILY_EXPORT_ENERGY(13045, UINT16, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.KILOWATT_HOUR),
|
||||
"grid-information"),
|
||||
TOTAL_EXPORT_ENERGY(13046, UINT32_SWAP, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.KILOWATT_HOUR),
|
||||
"grid-information");
|
||||
|
||||
/*
|
||||
* Status Registers -not known if working so not implemented yet.
|
||||
*
|
||||
*
|
||||
* INVERTER_ALARM(13050, UINT32_SWAP, 1, quantityTypeFactory(Units.ONE),
|
||||
* GRID_SIDE_FAULT(13052, UINT32_SWAP, 1, quantityTypeFactory(Units.ONE),
|
||||
* SYSTEM_FAULT_1(13054, UINT32_SWAP, 1, quantityTypeFactory(Units.ONE),
|
||||
* SYSTEM_FAULT_2(13056, UINT32_SWAP, 1, quantityTypeFactory(Units.ONE),
|
||||
* DC_SIDE_FAULT(13058, UINT32_SWAP, 1, quantityTypeFactory(Units.ONE),
|
||||
* PERMANENT_FAULT(13060, UINT32_SWAP, 1, quantityTypeFactory(Units.ONE),
|
||||
* BDC_SIDE_FAULT(13062, UINT32_SWAP, 1, quantityTypeFactory(Units.ONE),
|
||||
* BDC_SIDE_PERMANENT_FAULT(13064, UINT32_SWAP, 1, quantityTypeFactory(Units.ONE),
|
||||
* BATTERY_FAULT(13066, UINT32_SWAP, 1, quantityTypeFactory(Units.ONE),
|
||||
* BATTERY_ALARM(13068, UINT32_SWAP, 1, quantityTypeFactory(Units.ONE),
|
||||
* BMS_ALARM_1(13070, UINT32_SWAP, 1, quantityTypeFactory(Units.ONE),
|
||||
* BMS_PROTECTION(13072, UINT32_SWAP, 1, quantityTypeFactory(Units.ONE),
|
||||
* BMS_FAULT_1(13074, UINT32_SWAP, 1, quantityTypeFactory(Units.ONE),
|
||||
* BMS_FAULT_2(13076, UINT32_SWAP, 1, quantityTypeFactory(Units.ONE),
|
||||
* BMS_ALARM_2(13078, UINT32_SWAP, 1, quantityTypeFactory(Units.ONE);
|
||||
*/
|
||||
|
||||
private final BigDecimal multiplier;
|
||||
private final int registerNumber;
|
||||
private final ValueType type;
|
||||
|
||||
private final Function<BigDecimal, BigDecimal> conversion;
|
||||
private final Function<BigDecimal, State> stateFactory;
|
||||
private final String channelGroup;
|
||||
|
||||
SungrowInverterRegisters(int registerNumber, ValueType type, BigDecimal multiplier,
|
||||
Function<BigDecimal, State> stateFactory, Function<BigDecimal, BigDecimal> conversion,
|
||||
String channelGroup) {
|
||||
this.multiplier = multiplier;
|
||||
this.registerNumber = registerNumber;
|
||||
this.type = type;
|
||||
this.conversion = conversion;
|
||||
this.stateFactory = stateFactory;
|
||||
this.channelGroup = channelGroup;
|
||||
}
|
||||
|
||||
SungrowInverterRegisters(int registerNumber, ValueType type, BigDecimal multiplier,
|
||||
Function<BigDecimal, State> stateFactory, String channelGroup) {
|
||||
this.multiplier = multiplier;
|
||||
this.registerNumber = registerNumber;
|
||||
this.type = type;
|
||||
this.conversion = Function.identity();
|
||||
this.stateFactory = stateFactory;
|
||||
this.channelGroup = channelGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Function that creates {@link QuantityType} states with the given {@link Unit}.
|
||||
*
|
||||
* @param unit {@link Unit} to be used for the value.
|
||||
* @return Function for value creation.
|
||||
*/
|
||||
private static Function<BigDecimal, State> quantityFactory(Unit<?> unit) {
|
||||
return (BigDecimal value) -> new QuantityType<>(value, unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the modbus register number.
|
||||
*
|
||||
* @return modbus register number.
|
||||
*/
|
||||
public int getRegisterNumber() {
|
||||
return registerNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ValueType} for the channel.
|
||||
*
|
||||
* @return {@link ValueType} for the channel.
|
||||
*/
|
||||
public ValueType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the count of registers read to return the value of this register.
|
||||
*
|
||||
* @return register count.
|
||||
*/
|
||||
public int getRegisterCount() {
|
||||
return this.type.getBits() / 16;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the channel group.
|
||||
*
|
||||
* @return channel group id.
|
||||
*/
|
||||
public String getChannelGroup() {
|
||||
return channelGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the channel name.
|
||||
*
|
||||
* @return the channel name.
|
||||
*/
|
||||
public String getChannelName() {
|
||||
return this.name().toLowerCase().replace('_', '-');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@link State} for the given register value.
|
||||
*
|
||||
* @param registerValue the value for the channel.
|
||||
* @return {@link State] for the given value.
|
||||
*/
|
||||
public State createState(DecimalType registerValue) {
|
||||
final BigDecimal scaledValue = registerValue.toBigDecimal().multiply(this.multiplier);
|
||||
|
||||
final BigDecimal convertedValue = conversion.apply(scaledValue);
|
||||
return this.stateFactory.apply(convertedValue);
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
<?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="thing-type:sungrow:inverter">
|
||||
<parameter name="pollInterval" type="integer" required="true" min="100" unit="ms">
|
||||
<label>Poll Interval</label>
|
||||
<description>Time between polling the data in ms.</description>
|
||||
<default>5000</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</config-description:config-descriptions>
|
@ -0,0 +1,64 @@
|
||||
# thing types
|
||||
|
||||
thing-type.modbus.sungrowInverter.label = Sungrow inverter
|
||||
thing-type.modbus.sungrowInverter.description = Sungrow inverter connected via Modbus.
|
||||
|
||||
# thing types config
|
||||
|
||||
thing-type.config.sungrow.inverter.pollInterval.label = Poll Interval
|
||||
thing-type.config.sungrow.inverter.pollInterval.description = Time between polling the data in ms.
|
||||
|
||||
# channel groups
|
||||
channel-group-type.modbus.sg-overview.label = Overview
|
||||
channel-group-type.modbus.sg-mppt_information.label = MPPT
|
||||
channel-group-type.modbus.sg-battery_information.label = Battery
|
||||
channel-group-type.modbus.sg-load_information.label = Load
|
||||
channel-group-type.modbus.sg-grid_information.label = Grid
|
||||
|
||||
# channel types
|
||||
|
||||
channel-type.modbus.sg-battery_capacity.label = Battery Capacity
|
||||
channel-type.modbus.sg-battery_current.label = Battery Current
|
||||
channel-type.modbus.sg-battery_healthy.label = Battery Health (SOH)
|
||||
channel-type.modbus.sg-battery_level.label = Battery Level (SOC)
|
||||
channel-type.modbus.sg-battery_power.label = Battery Power
|
||||
channel-type.modbus.sg-battery_temperature.label = Battery Temperature
|
||||
channel-type.modbus.sg-battery_voltage.label = Battery Voltage
|
||||
channel-type.modbus.sg-co2_reduction.label = Co2 Reduction
|
||||
channel-type.modbus.sg-daily_battery_charge.label = Daily Battery Charging Energy from PV
|
||||
channel-type.modbus.sg-daily_battery_discharge_energy.label = Daily Battery Discharging Energy
|
||||
channel-type.modbus.sg-daily_charge_energy.label = Daily Battery Charging Energy
|
||||
channel-type.modbus.sg-daily_direct_energy_consumption.label = Daily Load Energy Consumption from PV
|
||||
channel-type.modbus.sg-daily_export_energy.label = Daily Feed-in Energy
|
||||
channel-type.modbus.sg-daily_export_power_from_pv.label = Daily Feed-in Energy (PV)
|
||||
channel-type.modbus.sg-daily_import_energy.label = Daily Purchased Energy
|
||||
channel-type.modbus.sg-daily_pv_generation.label = Daily PV Yield
|
||||
channel-type.modbus.sg-export_power.label = Total Export Active Power
|
||||
channel-type.modbus.sg-grid_frequency.label = Grid Frequency
|
||||
channel-type.modbus.sg-internal_temperature.label = Internal Temperature
|
||||
channel-type.modbus.sg-load_power.label = Load Power
|
||||
channel-type.modbus.sg-mppt1_current.label = MPPT1 Current
|
||||
channel-type.modbus.sg-mppt1_voltage.label = MPPT1 Voltage
|
||||
channel-type.modbus.sg-mppt2_current.label = MPPT2 Current
|
||||
channel-type.modbus.sg-mppt2_voltage.label = MPPT2 Voltage
|
||||
channel-type.modbus.sg-phase_a_current.label = Phase A Current
|
||||
channel-type.modbus.sg-phase_a_voltage.label = Phase A Voltage
|
||||
channel-type.modbus.sg-phase_b_current.label = Phase B Current
|
||||
channel-type.modbus.sg-phase_b_voltage.label = Phase B Voltage
|
||||
channel-type.modbus.sg-phase_c_current.label = Phase C Current
|
||||
channel-type.modbus.sg-phase_c_voltage.label = Phase C Voltage
|
||||
channel-type.modbus.sg-power_factor.label = Total Power Factor
|
||||
channel-type.modbus.sg-reactive_power.label = Total Reactive Power
|
||||
channel-type.modbus.sg-running_state.label = Running Status
|
||||
channel-type.modbus.sg-self_consumption_today.label = Daily Self-consumption Rate
|
||||
channel-type.modbus.sg-system_state.label = Current Status
|
||||
channel-type.modbus.sg-total_active_power.label = Total Active Power
|
||||
channel-type.modbus.sg-total_battery_charge.label = Total Battery Charging Energy
|
||||
channel-type.modbus.sg-total_battery_discharge_energy.label = Total Battery Discharging Energy
|
||||
channel-type.modbus.sg-total_charge_energy.label = Total Battery Charging Energy from PV
|
||||
channel-type.modbus.sg-total_dc_power.label = Total DC Power
|
||||
channel-type.modbus.sg-total_direct_energy_consumption.label = Total Load Energy Consumption from PV
|
||||
channel-type.modbus.sg-total_export_energy.label = Total Feed-in Energy
|
||||
channel-type.modbus.sg-total_export_energy_from_pv.label = Total Feed-in Energy (PV)
|
||||
channel-type.modbus.sg-total_import_energy.label = Total Purchased Energy
|
||||
channel-type.modbus.sg-total_pv_generation.label = Total PV Yield
|
@ -0,0 +1,507 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="modbus"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<thing-type id="sungrow-inverter">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="serial"/>
|
||||
<bridge-type-ref id="tcp"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Sungrow Inverter</label>
|
||||
<description>Sungrow inverter connected via Modbus.</description>
|
||||
<category>Inverter</category>
|
||||
<channel-groups>
|
||||
<channel-group id="sg-overview" typeId="sg-overview"/>
|
||||
<channel-group id="sg-mppt-information" typeId="sg-mppt-information"/>
|
||||
<channel-group id="sg-battery-information" typeId="sg-battery-information"/>
|
||||
<channel-group id="sg-grid-information" typeId="sg-grid-information"/>
|
||||
<channel-group id="sg-load-information" typeId="sg-load-information"/>
|
||||
</channel-groups>
|
||||
<config-description-ref uri="thing-type:sungrow:inverter"/>
|
||||
</thing-type>
|
||||
|
||||
<channel-group-type id="sg-overview">
|
||||
<label>Overview</label>
|
||||
<channels>
|
||||
<channel id="sg-total-active-power" typeId="sg-total-active-power"/>
|
||||
<channel id="sg-total-dc-power" typeId="sg-total-dc-power"/>
|
||||
<channel id="sg-phase-a-voltage" typeId="sg-phase-a-voltage"/>
|
||||
<channel id="sg-phase-b-voltage" typeId="sg-phase-b-voltage"/>
|
||||
<channel id="sg-phase-c-voltage" typeId="sg-phase-c-voltage"/>
|
||||
<channel id="sg-phase-a-current" typeId="sg-phase-a-current"/>
|
||||
<channel id="sg-phase-b-current" typeId="sg-phase-b-current"/>
|
||||
<channel id="sg-phase-c-current" typeId="sg-phase-c-current"/>
|
||||
<channel id="sg-reactive-power" typeId="sg-reactive-power"/>
|
||||
<channel id="sg-grid-frequency" typeId="sg-grid-frequency"/>
|
||||
<channel id="sg-daily-pv-generation" typeId="sg-daily-pv-generation"/>
|
||||
<channel id="sg-total-pv-generation" typeId="sg-total-pv-generation"/>
|
||||
<channel id="sg-power-factor" typeId="sg-power-factor"/>
|
||||
<channel id="sg-internal-temperature" typeId="sg-internal-temperature"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="sg-mppt-information">
|
||||
<label>MPPT Information</label>
|
||||
<channels>
|
||||
<channel id="sg-mppt1-voltage" typeId="sg-mppt1-voltage"/>
|
||||
<channel id="sg-mppt1-current" typeId="sg-mppt1-current"/>
|
||||
<channel id="sg-mppt2-voltage" typeId="sg-mppt2-voltage"/>
|
||||
<channel id="sg-mppt2-current" typeId="sg-mppt2-current"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="sg-battery-information">
|
||||
<label>Battery Information</label>
|
||||
<category>Battery</category>
|
||||
<channels>
|
||||
<channel id="sg-battery-voltage" typeId="sg-battery-voltage"/>
|
||||
<channel id="sg-battery-current" typeId="sg-battery-current"/>
|
||||
<channel id="sg-battery-power" typeId="sg-battery-power"/>
|
||||
<channel id="sg-battery-level" typeId="sg-battery-level"/>
|
||||
<channel id="sg-battery-healthy" typeId="sg-battery-healthy"/>
|
||||
<channel id="sg-battery-temperature" typeId="sg-battery-temperature"/>
|
||||
<channel id="sg-battery-capacity" typeId="sg-battery-capacity"/>
|
||||
<channel id="sg-daily-charge-energy" typeId="sg-daily-charge-energy"/>
|
||||
<channel id="sg-total-charge-energy" typeId="sg-total-charge-energy"/>
|
||||
<channel id="sg-daily-battery-charge" typeId="sg-daily-battery-charge"/>
|
||||
<channel id="sg-total-battery-charge" typeId="sg-total-battery-charge"/>
|
||||
<channel id="sg-daily-battery-discharge-energy" typeId="sg-daily-battery-discharge-energy"/>
|
||||
<channel id="sg-total-battery-discharge-energy" typeId="sg-total-battery-discharge-energy"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="sg-grid-information">
|
||||
<label>Grid Information</label>
|
||||
<channels>
|
||||
<channel id="sg-daily-export-energy" typeId="sg-daily-export-energy"/>
|
||||
<channel id="sg-total-export-energy" typeId="sg-total-export-energy"/>
|
||||
<channel id="sg-daily-import-energy" typeId="sg-daily-import-energy"/>
|
||||
<channel id="sg-total-import-energy" typeId="sg-total-import-energy"/>
|
||||
<channel id="sg-daily-export-power-from-pv" typeId="sg-daily-export-power-from-pv"/>
|
||||
<channel id="sg-total-export-energy-from-pv" typeId="sg-total-export-energy-from-pv"/>
|
||||
<channel id="sg-export-power" typeId="sg-export-power"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="sg-load-information">
|
||||
<label>Load Information</label>
|
||||
<channels>
|
||||
<channel id="sg-load-power" typeId="sg-load-power"/>
|
||||
<channel id="sg-daily-direct-energy-consumption" typeId="sg-daily-direct-energy-consumption"/>
|
||||
<channel id="sg-total-direct-energy-consumption" typeId="sg-total-direct-energy-consumption"/>
|
||||
<channel id="sg-self-consumption-today" typeId="sg-self-consumption-today"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-type id="sg-internal-temperature" advanced="true">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Internal Temperature</label>
|
||||
<category>Temperature</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Temperature</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-mppt1-voltage" advanced="true">
|
||||
<item-type>Number:ElectricPotential</item-type>
|
||||
<label>MPPT1 Voltage</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Voltage</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-mppt1-current" advanced="true">
|
||||
<item-type>Number:ElectricCurrent</item-type>
|
||||
<label>MPPT1 Current</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Current</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-mppt2-voltage" advanced="true">
|
||||
<item-type>Number:ElectricPotential</item-type>
|
||||
<label>MPPT2 Voltage</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Voltage</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-mppt2-current" advanced="true">
|
||||
<item-type>Number:ElectricCurrent</item-type>
|
||||
<label>MPPT2 Current</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Current</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-total-dc-power">
|
||||
<item-type>Number:Power</item-type>
|
||||
<label>Total DC Power</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Power</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-phase-a-voltage" advanced="true">
|
||||
<item-type>Number:ElectricPotential</item-type>
|
||||
<label>Phase A Voltage</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Voltage</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-phase-b-voltage" advanced="true">
|
||||
<item-type>Number:ElectricPotential</item-type>
|
||||
<label>Phase B Voltage</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Voltage</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-phase-c-voltage" advanced="true">
|
||||
<item-type>Number:ElectricPotential</item-type>
|
||||
<label>Phase C Voltage</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Voltage</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-reactive-power" advanced="true">
|
||||
<item-type>Number:Power</item-type>
|
||||
<label>Reactive Power</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Power</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-power-factor" advanced="true">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Power Factor</label>
|
||||
<category>Energy</category>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-grid-frequency" advanced="true">
|
||||
<item-type>Number:Frequency</item-type>
|
||||
<label>Grid Frequency</label>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Frequency</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="sg-daily-pv-generation">
|
||||
<item-type>Number:Energy</item-type>
|
||||
<label>Daily PV Generation</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Energy</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-total-pv-generation">
|
||||
<item-type>Number:Energy</item-type>
|
||||
<label>Total PV Generation</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Energy</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-daily-export-power-from-pv">
|
||||
<item-type>Number:Power</item-type>
|
||||
<label>Daily Export Power from PV</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Power</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-total-export-energy-from-pv">
|
||||
<item-type>Number:Energy</item-type>
|
||||
<label>Total Export Energy from PV</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Energy</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-load-power">
|
||||
<item-type>Number:Power</item-type>
|
||||
<label>Load Power</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Power</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-export-power">
|
||||
<item-type>Number:Power</item-type>
|
||||
<label>Export Power</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Power</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-daily-battery-charge">
|
||||
<item-type>Number:Energy</item-type>
|
||||
<label>Daily Battery Charge</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Energy</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-total-battery-charge">
|
||||
<item-type>Number:Energy</item-type>
|
||||
<label>Total Battery Charge</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Energy</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-daily-direct-energy-consumption">
|
||||
<item-type>Number:Energy</item-type>
|
||||
<label>Daily Direct Energy Consumption</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Energy</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-total-direct-energy-consumption">
|
||||
<item-type>Number:Energy</item-type>
|
||||
<label>Total Direct Energy Consumption</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Energy</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-battery-voltage" advanced="true">
|
||||
<item-type>Number:ElectricPotential</item-type>
|
||||
<label>Battery Voltage</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Voltage</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-battery-current" advanced="true">
|
||||
<item-type>Number:ElectricCurrent</item-type>
|
||||
<label>Battery Current</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Current</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-battery-power">
|
||||
<item-type>Number:Power</item-type>
|
||||
<label>Battery Power</label>
|
||||
<category>Battery</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Power</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-battery-level">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Battery Level</label>
|
||||
<category>Battery</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Energy</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-battery-healthy">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Battery Healthy</label>
|
||||
<category>Battery</category>
|
||||
<state pattern="%.2f %%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-battery-temperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Battery Temperature</label>
|
||||
<category>Battery</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Temperature</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-daily-battery-discharge-energy">
|
||||
<item-type>Number:Energy</item-type>
|
||||
<label>Daily Battery Discharge Energy</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Energy</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-total-battery-discharge-energy">
|
||||
<item-type>Number:Energy</item-type>
|
||||
<label>Total Battery Discharge Energy</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Energy</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-self-consumption-today">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Self Consumption Today</label>
|
||||
<state pattern="%.2f %%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-phase-a-current" advanced="true">
|
||||
<item-type>Number:ElectricCurrent</item-type>
|
||||
<label>Phase A Current</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Current</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-phase-b-current" advanced="true">
|
||||
<item-type>Number:ElectricCurrent</item-type>
|
||||
<label>Phase B Current</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Current</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-phase-c-current" advanced="true">
|
||||
<item-type>Number:ElectricCurrent</item-type>
|
||||
<label>Phase C Current</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Current</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-total-active-power">
|
||||
<item-type>Number:Power</item-type>
|
||||
<label>Total Active Power</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Power</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-daily-import-energy">
|
||||
<item-type>Number:Energy</item-type>
|
||||
<label>Daily Import Energy</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Energy</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-total-import-energy">
|
||||
<item-type>Number:Energy</item-type>
|
||||
<label>Total Import Energy</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Energy</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-battery-capacity">
|
||||
<item-type>Number:Energy</item-type>
|
||||
<label>Battery Capacity</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Energy</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-daily-charge-energy">
|
||||
<item-type>Number:Energy</item-type>
|
||||
<label>Daily Charge Energy</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Energy</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-total-charge-energy">
|
||||
<item-type>Number:Energy</item-type>
|
||||
<label>Total Charge Energy</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Energy</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="sg-daily-export-energy">
|
||||
<item-type>Number:Energy</item-type>
|
||||
<label>Daily Export Energy</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Energy</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="sg-total-export-energy">
|
||||
<item-type>Number:Energy</item-type>
|
||||
<label>Total Export Energy</label>
|
||||
<category>Energy</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Energy</tag>
|
||||
</tags>
|
||||
<state pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
</thing:thing-descriptions>
|
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* 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.modbus.sungrow.internal;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.core.io.transport.modbus.ModbusBitUtilities;
|
||||
import org.openhab.core.io.transport.modbus.ModbusRegisterArray;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.types.State;
|
||||
|
||||
/**
|
||||
* @author Sönke Küper - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class SungrowInverterRegistersTest {
|
||||
@Test
|
||||
public void testCreatePercentTypeState() {
|
||||
SungrowInverterRegisters batteryLevelRegister = SungrowInverterRegisters.BATTERY_LEVEL;
|
||||
|
||||
ModbusRegisterArray registers = new ModbusRegisterArray(1000);
|
||||
Optional<DecimalType> value = ModbusBitUtilities.extractStateFromRegisters( //
|
||||
registers, //
|
||||
0, //
|
||||
batteryLevelRegister.getType() //
|
||||
);
|
||||
assertTrue(value.isPresent());
|
||||
DecimalType decimalTypeValue = value.get();
|
||||
// Value is not scaled yet
|
||||
assertEquals(BigDecimal.valueOf(1000), decimalTypeValue.toBigDecimal());
|
||||
|
||||
State state = batteryLevelRegister.createState(decimalTypeValue);
|
||||
assertInstanceOf(PercentType.class, state);
|
||||
assertEquals("100.0", state.toFullString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateQuantityTypeState() {
|
||||
SungrowInverterRegisters mpttVoltage = SungrowInverterRegisters.MPPT1_VOLTAGE;
|
||||
|
||||
ModbusRegisterArray registers = new ModbusRegisterArray(1234);
|
||||
Optional<DecimalType> value = ModbusBitUtilities.extractStateFromRegisters( //
|
||||
registers, //
|
||||
0, //
|
||||
mpttVoltage.getType() //
|
||||
);
|
||||
assertTrue(value.isPresent());
|
||||
DecimalType decimalTypeValue = value.get();
|
||||
// Value is not scaled yet
|
||||
assertEquals(BigDecimal.valueOf(1234), decimalTypeValue.toBigDecimal());
|
||||
|
||||
State state = mpttVoltage.createState(decimalTypeValue);
|
||||
assertInstanceOf(QuantityType.class, state);
|
||||
assertEquals("123.4 V", state.toFullString());
|
||||
}
|
||||
}
|
@ -249,6 +249,7 @@
|
||||
<module>org.openhab.binding.modbus.e3dc</module>
|
||||
<module>org.openhab.binding.modbus.sbc</module>
|
||||
<module>org.openhab.binding.modbus.studer</module>
|
||||
<module>org.openhab.binding.modbus.sungrow</module>
|
||||
<module>org.openhab.binding.modbus.sunspec</module>
|
||||
<module>org.openhab.binding.modbus.stiebeleltron</module>
|
||||
<module>org.openhab.binding.modbus.helioseasycontrols</module>
|
||||
|
Loading…
Reference in New Issue
Block a user