From 3f3ba2cfe3ec3a7c6b65c46d6b27989efd40f680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20K=C3=BCper?= Date: Mon, 18 Dec 2023 21:15:06 +0100 Subject: [PATCH 01/30] 0000: Generated Binding Skeleton MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- CODEOWNERS | 1 + bom/openhab-addons/pom.xml | 5 + .../org.openhab.binding.modbus.sungrow/NOTICE | 13 +++ .../README.md | 94 ++++++++++++++++ .../pom.xml | 17 +++ .../src/main/feature/feature.xml | 9 ++ .../ModbusSungrowBindingConstants.java | 34 ++++++ .../internal/ModbusSungrowConfiguration.java | 31 ++++++ .../internal/ModbusSungrowHandler.java | 104 ++++++++++++++++++ .../internal/ModbusSungrowHandlerFactory.java | 55 +++++++++ .../src/main/resources/OH-INF/addon/addon.xml | 10 ++ .../resources/OH-INF/i18n/sungrow.properties | 3 + .../resources/OH-INF/thing/thing-types.xml | 48 ++++++++ bundles/pom.xml | 1 + 14 files changed, 425 insertions(+) create mode 100644 bundles/org.openhab.binding.modbus.sungrow/NOTICE create mode 100644 bundles/org.openhab.binding.modbus.sungrow/README.md create mode 100644 bundles/org.openhab.binding.modbus.sungrow/pom.xml create mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/main/feature/feature.xml create mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowBindingConstants.java create mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowConfiguration.java create mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowHandler.java create mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowHandlerFactory.java create mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/addon/addon.xml create mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/i18n/sungrow.properties create mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml diff --git a/CODEOWNERS b/CODEOWNERS index 59a56ddd9f1d3..77d9a049371b8 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -216,6 +216,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@gmx.de /bundles/org.openhab.binding.modbus.sunspec/ @mrbig /bundles/org.openhab.binding.monopriceaudio/ @mlobstein /bundles/org.openhab.binding.mpd/ @stefanroellin diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index dd5cb09900a0a..520625b1380bb 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -1071,6 +1071,11 @@ org.openhab.binding.modbus.studer ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.modbus.sungrow + ${project.version} + org.openhab.addons.bundles org.openhab.binding.modbus.sunspec diff --git a/bundles/org.openhab.binding.modbus.sungrow/NOTICE b/bundles/org.openhab.binding.modbus.sungrow/NOTICE new file mode 100644 index 0000000000000..38d625e349232 --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sungrow/NOTICE @@ -0,0 +1,13 @@ +This content is produced and maintained by the openHAB project. + +* Project home: https://www.openhab.org + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/openhab/openhab-addons diff --git a/bundles/org.openhab.binding.modbus.sungrow/README.md b/bundles/org.openhab.binding.modbus.sungrow/README.md new file mode 100644 index 0000000000000..4e4deddbce776 --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sungrow/README.md @@ -0,0 +1,94 @@ +# Modbus Sungrow Binding + +_Give some details about what this binding is meant for - a protocol, system, specific device._ + +_If possible, provide some resources like pictures (only PNG is supported currently), a video, etc. to give an impression of what can be done with this binding._ +_You can place such resources into a `doc` folder next to this README.md._ + +_Put each sentence in a separate line to improve readability of diffs._ + +## Supported Things + +_Please describe the different supported things / devices including their ThingTypeUID within this section._ +_Which different types are supported, which models were tested etc.?_ +_Note that it is planned to generate some part of this based on the XML files within ```src/main/resources/OH-INF/thing``` of your binding._ + +- `bridge`: Short description of the Bridge, if any +- `sample`: Short description of the Thing with the ThingTypeUID `sample` + +## Discovery + +_Describe the available auto-discovery features here._ +_Mention for what it works and what needs to be kept in mind when using it._ + +## Binding Configuration + +_If your binding requires or supports general configuration settings, please create a folder ```cfg``` and place the configuration file ```.cfg``` inside it._ +_In this section, you should link to this file and provide some information about the options._ +_The file could e.g. look like:_ + +``` +# Configuration for the ModbusSungrow Binding +# +# Default secret key for the pairing of the ModbusSungrow Thing. +# It has to be between 10-40 (alphanumeric) characters. +# This may be changed by the user for security reasons. +secret=openHABSecret +``` + +_Note that it is planned to generate some part of this based on the information that is available within ```src/main/resources/OH-INF/binding``` of your binding._ + +_If your binding does not offer any generic configurations, you can remove this section completely._ + +## Thing Configuration + +_Describe what is needed to manually configure a thing, either through the UI or via a thing-file._ +_This should be mainly about its mandatory and optional configuration parameters._ + +_Note that it is planned to generate some part of this based on the XML files within ```src/main/resources/OH-INF/thing``` of your binding._ + +### `sample` Thing Configuration + +| Name | Type | Description | Default | Required | Advanced | +|-----------------|---------|---------------------------------------|---------|----------|----------| +| hostname | text | Hostname or IP address of the device | N/A | yes | no | +| password | text | Password to access the device | N/A | yes | no | +| refreshInterval | integer | Interval the device is polled in sec. | 600 | no | yes | + +## Channels + +_Here you should provide information about available channel types, what their meaning is and how they can be used._ + +_Note that it is planned to generate some part of this based on the XML files within ```src/main/resources/OH-INF/thing``` of your binding._ + +| Channel | Type | Read/Write | Description | +|---------|--------|------------|-----------------------------| +| control | Switch | RW | This is the control channel | + +## Full Example + +_Provide a full usage example based on textual configuration files._ +_*.things, *.items examples are mandatory as textual configuration is well used by many users._ +_*.sitemap examples are optional._ + +### Thing Configuration + +```java +Example thing configuration goes here. +``` +### Item Configuration + +```java +Example item configuration goes here. +``` + +### Sitemap Configuration + +```perl +Optional Sitemap configuration goes here. +Remove this section, if not needed. +``` + +## Any custom content here! + +_Feel free to add additional sections for whatever you think should also be mentioned about your binding!_ diff --git a/bundles/org.openhab.binding.modbus.sungrow/pom.xml b/bundles/org.openhab.binding.modbus.sungrow/pom.xml new file mode 100644 index 0000000000000..39d6482b66ddb --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sungrow/pom.xml @@ -0,0 +1,17 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 4.0.0-SNAPSHOT + + + org.openhab.binding.modbus.sungrow + + openHAB Add-ons :: Bundles :: Modbus Sungrow Binding + + diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/feature/feature.xml b/bundles/org.openhab.binding.modbus.sungrow/src/main/feature/feature.xml new file mode 100644 index 0000000000000..d7f10bdbccdc0 --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/feature/feature.xml @@ -0,0 +1,9 @@ + + + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features + + + openhab-runtime-base + mvn:org.openhab.addons.bundles/org.openhab.binding.modbus.sungrow/${project.version} + + diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowBindingConstants.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowBindingConstants.java new file mode 100644 index 0000000000000..a295e416173ad --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowBindingConstants.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.modbus.sungrow.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +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 { + + private static final String BINDING_ID = "sungrow"; + + // List of all Thing Type UIDs + public static final ThingTypeUID THING_TYPE_SAMPLE = new ThingTypeUID(BINDING_ID, "sample"); + + // List of all Channel ids + public static final String CHANNEL_1 = "channel1"; +} diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowConfiguration.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowConfiguration.java new file mode 100644 index 0000000000000..3704c2ff7688b --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowConfiguration.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.modbus.sungrow.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link ModbusSungrowConfiguration} class contains fields mapping thing configuration parameters. + * + * @author Sönke Küper - Initial contribution + */ +@NonNullByDefault +public class ModbusSungrowConfiguration { + + /** + * Sample configuration parameters. Replace with your own. + */ + public String hostname = ""; + public String password = ""; + public int refreshInterval = 600; +} diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowHandler.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowHandler.java new file mode 100644 index 0000000000000..6b2c464ab812a --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowHandler.java @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.modbus.sungrow.internal; + +import static org.openhab.binding.modbus.sungrow.internal.ModbusSungrowBindingConstants.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.binding.BaseThingHandler; +import org.openhab.core.types.Command; +import org.openhab.core.types.RefreshType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link ModbusSungrowHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Sönke Küper - Initial contribution + */ +@NonNullByDefault +public class ModbusSungrowHandler extends BaseThingHandler { + + private final Logger logger = LoggerFactory.getLogger(ModbusSungrowHandler.class); + + private @Nullable ModbusSungrowConfiguration config; + + public ModbusSungrowHandler(Thing thing) { + super(thing); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (CHANNEL_1.equals(channelUID.getId())) { + if (command instanceof RefreshType) { + // TODO: handle data refresh + } + + // TODO: handle command + + // Note: if communication with thing fails for some reason, + // indicate that by setting the status with detail information: + // updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + // "Could not control device at IP address x.x.x.x"); + } + } + + @Override + public void initialize() { + config = getConfigAs(ModbusSungrowConfiguration.class); + + // TODO: Initialize the handler. + // The framework requires you to return from this method quickly, i.e. any network access must be done in + // the background initialization below. + // Also, before leaving this method a thing status from one of ONLINE, OFFLINE or UNKNOWN must be set. This + // might already be the real thing status in case you can decide it directly. + // In case you can not decide the thing status directly (e.g. for long running connection handshake using WAN + // access or similar) you should set status UNKNOWN here and then decide the real status asynchronously in the + // background. + + // set the thing status to UNKNOWN temporarily and let the background task decide for the real status. + // the framework is then able to reuse the resources from the thing handler initialization. + // we set this upfront to reliably check status updates in unit tests. + updateStatus(ThingStatus.UNKNOWN); + + // Example for background initialization: + scheduler.execute(() -> { + boolean thingReachable = true; // + // when done do: + if (thingReachable) { + updateStatus(ThingStatus.ONLINE); + } else { + updateStatus(ThingStatus.OFFLINE); + } + }); + + // These logging types should be primarily used by bindings + // logger.trace("Example trace message"); + // logger.debug("Example debug message"); + // logger.warn("Example warn message"); + // + // Logging to INFO should be avoided normally. + // See https://www.openhab.org/docs/developer/guidelines.html#f-logging + + // Note: When initialization can NOT be done set the status with more details for further + // analysis. See also class ThingStatusDetail for all available status details. + // Add a description to give user information to understand why thing does not work as expected. E.g. + // updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + // "Can not access device as username and/or password are invalid"); + } +} diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowHandlerFactory.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowHandlerFactory.java new file mode 100644 index 0000000000000..4761494a0dd08 --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowHandlerFactory.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.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 SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_SAMPLE); + + @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_SAMPLE.equals(thingTypeUID)) { + return new ModbusSungrowHandler(thing); + } + + return null; + } +} diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/addon/addon.xml new file mode 100644 index 0000000000000..4bc7f3f038e93 --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/addon/addon.xml @@ -0,0 +1,10 @@ + + + + binding + Modbus Sungrow Binding + This is the binding for Modbus Sungrow. + + diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/i18n/sungrow.properties b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/i18n/sungrow.properties new file mode 100644 index 0000000000000..0c2f44015a92d --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/i18n/sungrow.properties @@ -0,0 +1,3 @@ +# FIXME: please add all English translations to this file so the texts can be translated using Crowdin +# FIXME: to generate the content of this file run: mvn i18n:generate-default-translations +# FIXME: see also: https://www.openhab.org/docs/developer/utils/i18n.html diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml new file mode 100644 index 0000000000000..a6aa2b5d12bf3 --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml @@ -0,0 +1,48 @@ + + + + + + + + + Sample thing for Modbus Sungrow Binding + + + + + + + + network-address + + Hostname or IP address of the device + + + password + + Password to access the device + + + + Interval the device is polled in sec. + 600 + true + + + + + + + Number:Temperature + + Sample channel for ModbusSungrow Binding + + diff --git a/bundles/pom.xml b/bundles/pom.xml index 7ded2a83bd725..ee828b4d706f0 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -246,6 +246,7 @@ org.openhab.binding.modbus.e3dc org.openhab.binding.modbus.sbc org.openhab.binding.modbus.studer + org.openhab.binding.modbus.sungrow org.openhab.binding.modbus.sunspec org.openhab.binding.modbus.stiebeleltron org.openhab.binding.modbus.helioseasycontrols From 6b649baf19e5455c1a58501f6406409ca4b0da84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20K=C3=BCper?= Date: Mon, 18 Dec 2023 21:15:06 +0100 Subject: [PATCH 02/30] 0000: Implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- .../README.md | 1 + .../pom.xml | 10 + .../src/main/feature/feature.xml | 9 - .../sungrow/internal/ConversionConstants.java | 36 ++ .../sungrow/internal/DeviceTypeCode.java | 78 ++++ .../internal/DeviceTypeCodeBackup.java | 132 ++++++ .../ModbusSungrowBindingConstants.java | 13 +- .../internal/ModbusSungrowHandler.java | 104 ----- .../internal/ModbusSungrowHandlerFactory.java | 6 +- ...java => SungrowInverterConfiguration.java} | 11 +- .../internal/SungrowInverterHandler.java | 227 ++++++++++ .../internal/SungrowInverterRegisters.java | 172 +++++++ .../SungrowInverterRegistersBackup.java | 120 +++++ .../modbus/sungrow/internal/SystemState.java | 73 +++ .../src/main/resources/OH-INF/addon/addon.xml | 10 - .../main/resources/OH-INF/config/config.xml | 15 + .../resources/OH-INF/i18n/sungrow.properties | 79 +++- .../OH-INF/i18n/sungrow_de.properties | 76 ++++ .../resources/OH-INF/thing/thing-types.xml | 427 ++++++++++++++++-- .../SungrowInverterRegistersTest.java | 40 ++ 20 files changed, 1460 insertions(+), 179 deletions(-) delete mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/main/feature/feature.xml create mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ConversionConstants.java create mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/DeviceTypeCode.java create mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/DeviceTypeCodeBackup.java delete mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowHandler.java rename bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/{ModbusSungrowConfiguration.java => SungrowInverterConfiguration.java} (63%) create mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterHandler.java create mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java create mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersBackup.java create mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SystemState.java delete mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/addon/addon.xml create mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/config/config.xml create mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/i18n/sungrow_de.properties create mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/test/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersTest.java diff --git a/bundles/org.openhab.binding.modbus.sungrow/README.md b/bundles/org.openhab.binding.modbus.sungrow/README.md index 4e4deddbce776..3dc49ad8d06a3 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/README.md +++ b/bundles/org.openhab.binding.modbus.sungrow/README.md @@ -76,6 +76,7 @@ _*.sitemap examples are optional._ ```java Example thing configuration goes here. ``` + ### Item Configuration ```java diff --git a/bundles/org.openhab.binding.modbus.sungrow/pom.xml b/bundles/org.openhab.binding.modbus.sungrow/pom.xml index 39d6482b66ddb..a9be18c4c86e0 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/pom.xml +++ b/bundles/org.openhab.binding.modbus.sungrow/pom.xml @@ -14,4 +14,14 @@ openHAB Add-ons :: Bundles :: Modbus Sungrow Binding + + + org.openhab.addons.bundles + org.openhab.binding.modbus + ${project.version} + provided + + + + diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/feature/feature.xml b/bundles/org.openhab.binding.modbus.sungrow/src/main/feature/feature.xml deleted file mode 100644 index d7f10bdbccdc0..0000000000000 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/feature/feature.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features - - - openhab-runtime-base - mvn:org.openhab.addons.bundles/org.openhab.binding.modbus.sungrow/${project.version} - - diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ConversionConstants.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ConversionConstants.java new file mode 100644 index 0000000000000..396bff31a5d67 --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ConversionConstants.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.modbus.sungrow.internal; + +import java.math.BigDecimal; +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() { + } + + /** + * Value conversion from Celsius to Kelvin. + */ + static final Function CELSIUS_TO_KELVIN = (BigDecimal celsius) -> celsius + .add(new BigDecimal(273.15f)); +} diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/DeviceTypeCode.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/DeviceTypeCode.java new file mode 100644 index 0000000000000..a4da1e4bdf15f --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/DeviceTypeCode.java @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.modbus.sungrow.internal; + +import java.util.Arrays; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Definition of Sungrow Device codes. + * + * @author Sönke Küper - Initial contribution + */ +@NonNullByDefault +public enum DeviceTypeCode { + + SH3K6("SH3K6", 0xD06), + SH4K6("SH4K6", 0xD07), + SH5K_20("SH5K-20", 0xD09), + SH5K_V13("SH5K-V13", 0xD03), + SH3K6_30("SH3K6-30", 0xD0A), + SH4K6_30("SH4K6-30", 0xD0B), + SH5K_30("SH5K-30", 0xD0C), + SH3_0RS("SH3.0RS", 0xD17), + SH3_6RS("SH3.6RS", 0xD0D), + SH4_0RS("SH4.0RS", 0xD18), + SH5_0RS("SH5.0RS", 0xD0F), + SH6_0RS("SH6.0RS", 0xD10), + SH5_0RT("SH5.0RT", 0xE00), + SH6_0RT("SH6.0RT", 0xE01), + SH8_0RT("SH8.0RT", 0xE02), + SH10RT("SH10RT", 0xE03); + + private static final Map CODE_MAPPING = initMapping(); + + private static Map initMapping() { + return Arrays.stream(DeviceTypeCode.values()) + .collect(Collectors.toMap(DeviceTypeCode::getTypeCode, Function.identity())); + } + + /** + * Returns the DeviceTypeCodes for the given type code. + */ + @Nullable + public static DeviceTypeCode getByTypeCode(int typeCode) { + return CODE_MAPPING.get(typeCode); + } + + private final int typeCode; + private final String model; + + DeviceTypeCode(String model, int typeCode) { + this.typeCode = typeCode; + this.model = model; + } + + public int getTypeCode() { + return typeCode; + } + + public String getModel() { + return model; + } +} diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/DeviceTypeCodeBackup.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/DeviceTypeCodeBackup.java new file mode 100644 index 0000000000000..fbc4fb70b3d1e --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/DeviceTypeCodeBackup.java @@ -0,0 +1,132 @@ +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.modbus.sungrow.internal; + +import java.util.Arrays; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Definition of Sungrow Device codes. + * + * @author Sönke Küper - Initial contribution + */ +@NonNullByDefault +public enum DeviceTypeCodeBackup { + + SG30KTL(0x27, "SG30KTL", 2), + SG10KTL(0x26, "SG10KTL", 2), + SG12KTL(0x29, "SG12KTL", 2), + SG15KTL(0x28, "SG15KTL", 2), + SG20KTL(0x2A, "SG20KTL", 2), + SG30KU(0x2C, "SG30KU", 2), + SG36KTL(0x2D, "SG36KTL", 2), + SG36KU(0x2E, "SG36KU", 2), + SG40KTL(0x2F, "SG40KTL", 2), + SG40KTL_M(0x0135, "SG40KTL-M", 3), + SG50KTL_M(0x011B, "SG50KTL-M", 4), + SG60KTL_M(0x0131, "SG60KTL-M", 4), + SG60KU(0x0136, "SG60KU", 1), + SG30KTL_M(0x0141, "SG30KTL-M", 3), + SG30KTL_M_V31(0x70, "SG30KTL-M-V31", 3), + SG33KTL_M(0x0134, "SG33KTL-M", 3), + SG36KTL_M(0x74, "SG36KTL-M", 3), + SG33K3J(0x013D, "SG33K3J", 3), + SG49K5J(0x0137, "SG49K5J", 4), + SG34KJ(0x72, "SG34KJ", 2), + LP_P34KSG(0x73, "LP_P34KSG", 1), + // Duplicate typeCode: SG50KTL_M_20(0x011B, "SG50KTL-M-20", 4), + SG60KTL(0x010F, "SG60KTL", 1), + SG80KTL(0x0138, "SG80KTL", 1), + // Duplicate typeCode: SG80KTL_20(0x0138, "SG80KTL-20", 1), + SG60KU_M(0x0132, "SG60KU-M", 4), + SG5KTL_MT(0x0147, "SG5KTL-MT", 2), + SG6KTL_MT(0x0148, "SG6KTL-MT", 2), + SG8KTL_M(0x013F, "SG8KTL-M", 2), + SG10KTL_M(0x013E, "SG10KTL-M", 2), + SG10KTL_MT(0x2C0F, "SG10KTL-MT", 2), + SG12KTL_M(0x013C, "SG12KTL-M", 2), + SG15KTL_M(0x0142, "SG15KTL-M", 2), + SG17KTL_M(0x0149, "SG17KTL-M", 2), + SG20KTL_M(0x0143, "SG20KTL-M", 2), + SG80KTL_M(0x0139, "SG80KTL-M", 4), + SG111HV(0x014C, "SG111HV", 1), + SG125HV(0x013B, "SG125HV", 1), + SG125HV_20(0x2C03, "SG125HV-20", 1), + SG30CX(0x2C10, "SG30CX", 3), + SG33CX(0x2C00, "SG33CX", 3), + SG36CX_US(0x2C0A, "SG36CX-US", 3), + SG40CX(0x2C01, "SG40CX", 4), + SG50CX(0x2C02, "SG50CX", 5), + SG60CX_US(0x2C0B, "SG60CX-US", 5), + SG110CX(0x2C06, "SG110CX", 9), + SG250HX(0x2C0C, "SG250HX", 1), + SG250HX_US(0x2C11, "SG250HX-US", 1), + SG100CX(0x2C12, "SG100CX", 1), + // SG100CX_JP(0x2C12, "SG100CX-JP", 1), + SG250HX_IN(0x2C13, "SG250HX-IN", 1), + SG25CX_SA(0x2C15, "SG25CX-SA", 3), + SG75CX(0x2C22, "SG75CX", 9), + SG3_0RT(0x243D, "SG3.0RT", 2), + SG4_0RT(0x243E, "SG4.0RT", 2), + SG5_0RT(0x2430, "SG5.0RT", 2), + SG6_0RT(0x2431, "SG6.0RT", 2), + SG7_0RT(0x243C, "SG7.0RT", 2), + SG8_0RT(0x2432, "SG8.0RT", 2), + SG10RT(0x2433, "SG10RT", 2), + SG12RT(0x2434, "SG12RT", 2), + SG15RT(0x2435, "SG15RT", 2), + SG17RT(0x2436, "SG17RT", 2), + SG20RT(0x2437, "SG20RT", 2); + + private static final Map CODE_MAPPING = initMapping(); + + private static Map initMapping() { + return Arrays.stream(DeviceTypeCodeBackup.values()) + .collect(Collectors.toMap(DeviceTypeCodeBackup::getTypeCode, Function.identity())); + } + + /** + * Returns the DeviceTypeCodes for the given type code. + */ + @Nullable + public static DeviceTypeCodeBackup getByTypeCode(int typeCode) { + return CODE_MAPPING.get(typeCode); + } + + private final int typeCode; + private final String model; + private final int mpptCount; + + DeviceTypeCodeBackup(int typeCode, String model, int mpptCount) { + this.typeCode = typeCode; + this.model = model; + this.mpptCount = mpptCount; + } + + public int getMpptCount() { + return mpptCount; + } + + public int getTypeCode() { + return typeCode; + } + + public String getModel() { + return model; + } +} diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowBindingConstants.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowBindingConstants.java index a295e416173ad..c1cd6350ddad0 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowBindingConstants.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowBindingConstants.java @@ -13,6 +13,7 @@ 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; /** @@ -24,11 +25,9 @@ @NonNullByDefault public class ModbusSungrowBindingConstants { - private static final String BINDING_ID = "sungrow"; - - // List of all Thing Type UIDs - public static final ThingTypeUID THING_TYPE_SAMPLE = new ThingTypeUID(BINDING_ID, "sample"); - - // List of all Channel ids - public static final String CHANNEL_1 = "channel1"; + /** + * ThingType-ID for Inverter. + */ + public static final ThingTypeUID THING_TYPE_INVERTER = new ThingTypeUID(ModbusBindingConstants.BINDING_ID, + "sungrowInverter"); } diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowHandler.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowHandler.java deleted file mode 100644 index 6b2c464ab812a..0000000000000 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowHandler.java +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright (c) 2010-2023 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.modbus.sungrow.internal; - -import static org.openhab.binding.modbus.sungrow.internal.ModbusSungrowBindingConstants.*; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.core.thing.ChannelUID; -import org.openhab.core.thing.Thing; -import org.openhab.core.thing.ThingStatus; -import org.openhab.core.thing.binding.BaseThingHandler; -import org.openhab.core.types.Command; -import org.openhab.core.types.RefreshType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * The {@link ModbusSungrowHandler} is responsible for handling commands, which are - * sent to one of the channels. - * - * @author Sönke Küper - Initial contribution - */ -@NonNullByDefault -public class ModbusSungrowHandler extends BaseThingHandler { - - private final Logger logger = LoggerFactory.getLogger(ModbusSungrowHandler.class); - - private @Nullable ModbusSungrowConfiguration config; - - public ModbusSungrowHandler(Thing thing) { - super(thing); - } - - @Override - public void handleCommand(ChannelUID channelUID, Command command) { - if (CHANNEL_1.equals(channelUID.getId())) { - if (command instanceof RefreshType) { - // TODO: handle data refresh - } - - // TODO: handle command - - // Note: if communication with thing fails for some reason, - // indicate that by setting the status with detail information: - // updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - // "Could not control device at IP address x.x.x.x"); - } - } - - @Override - public void initialize() { - config = getConfigAs(ModbusSungrowConfiguration.class); - - // TODO: Initialize the handler. - // The framework requires you to return from this method quickly, i.e. any network access must be done in - // the background initialization below. - // Also, before leaving this method a thing status from one of ONLINE, OFFLINE or UNKNOWN must be set. This - // might already be the real thing status in case you can decide it directly. - // In case you can not decide the thing status directly (e.g. for long running connection handshake using WAN - // access or similar) you should set status UNKNOWN here and then decide the real status asynchronously in the - // background. - - // set the thing status to UNKNOWN temporarily and let the background task decide for the real status. - // the framework is then able to reuse the resources from the thing handler initialization. - // we set this upfront to reliably check status updates in unit tests. - updateStatus(ThingStatus.UNKNOWN); - - // Example for background initialization: - scheduler.execute(() -> { - boolean thingReachable = true; // - // when done do: - if (thingReachable) { - updateStatus(ThingStatus.ONLINE); - } else { - updateStatus(ThingStatus.OFFLINE); - } - }); - - // These logging types should be primarily used by bindings - // logger.trace("Example trace message"); - // logger.debug("Example debug message"); - // logger.warn("Example warn message"); - // - // Logging to INFO should be avoided normally. - // See https://www.openhab.org/docs/developer/guidelines.html#f-logging - - // Note: When initialization can NOT be done set the status with more details for further - // analysis. See also class ThingStatusDetail for all available status details. - // Add a description to give user information to understand why thing does not work as expected. E.g. - // updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - // "Can not access device as username and/or password are invalid"); - } -} diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowHandlerFactory.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowHandlerFactory.java index 4761494a0dd08..a6a7a9c03855f 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowHandlerFactory.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowHandlerFactory.java @@ -35,7 +35,7 @@ @Component(configurationPid = "binding.sungrow", service = ThingHandlerFactory.class) public class ModbusSungrowHandlerFactory extends BaseThingHandlerFactory { - private static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_SAMPLE); + private static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_INVERTER); @Override public boolean supportsThingType(ThingTypeUID thingTypeUID) { @@ -46,8 +46,8 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { protected @Nullable ThingHandler createHandler(Thing thing) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); - if (THING_TYPE_SAMPLE.equals(thingTypeUID)) { - return new ModbusSungrowHandler(thing); + if (THING_TYPE_INVERTER.equals(thingTypeUID)) { + return new SungrowInverterHandler(thing); } return null; diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowConfiguration.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterConfiguration.java similarity index 63% rename from bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowConfiguration.java rename to bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterConfiguration.java index 3704c2ff7688b..c58103b77e1ba 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowConfiguration.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterConfiguration.java @@ -15,17 +15,12 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * The {@link ModbusSungrowConfiguration} class contains fields mapping thing configuration parameters. + * The {@link SungrowInverterConfiguration} class contains fields mapping thing configuration parameters. * * @author Sönke Küper - Initial contribution */ @NonNullByDefault -public class ModbusSungrowConfiguration { +public class SungrowInverterConfiguration { - /** - * Sample configuration parameters. Replace with your own. - */ - public String hostname = ""; - public String password = ""; - public int refreshInterval = 600; + public int pollInterval; } diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterHandler.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterHandler.java new file mode 100644 index 0000000000000..d8f6b3bd45360 --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterHandler.java @@ -0,0 +1,227 @@ +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.modbus.sungrow.internal; + +import java.nio.charset.StandardCharsets; +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.library.types.QuantityType; +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 registers; + private final ModbusReadRequestBlueprint blueprint; + + public ModbusRequest(Deque registers, int slaveId) { + this.registers = registers; + this.blueprint = initReadRequest(registers, slaveId); + } + + private ModbusReadRequestBlueprint initReadRequest(Deque 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 static final int SERIAL_NUMBER_REGISTER = 4990; + + private final Logger logger = LoggerFactory.getLogger(SungrowInverterHandler.class); + + private static final int TRIES = 1; + private List modbusRequests = new ArrayList<>(); + + public SungrowInverterHandler(Thing thing) { + super(thing); + } + + /** + * Splits the SungrowInverterRegisters into multiple ModbusRequest, to ensure the max request size. + */ + private List buildRequests() { + final List requests = new ArrayList<>(); + Deque 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.size() > 0) { + 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; + } + + updateThingProperties(); + 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 updateThingProperties() { + final ModbusReadRequestBlueprint informationBlueprint = new ModbusReadRequestBlueprint(getSlaveId(), + ModbusReadFunctionCode.READ_INPUT_REGISTERS, SERIAL_NUMBER_REGISTER - 1, 13, TRIES); + submitOneTimePoll(informationBlueprint, this::updateThingProperties, this::readError); + } + + private void updateThingProperties(AsyncModbusReadResult result) { + result.getRegisters().ifPresent(registers -> { + final byte[] serialBytes = new byte[10]; + System.arraycopy(registers.getBytes(), 0, serialBytes, 0, 10); + String serialNumber = new String(serialBytes, StandardCharsets.UTF_8); + thing.setProperty("serialNumber", serialNumber); + + final int deviceTypeCode = ModbusBitUtilities.extractUInt16(registers.getBytes(), 10); + final DeviceTypeCode typeCode = DeviceTypeCode.getByTypeCode(deviceTypeCode); + if (typeCode == null) { + thing.setProperty("model", "unknown device type: " + deviceTypeCode); + } else { + thing.setProperty("model", typeCode.getModel()); + } + + final float nominalOutputPower = ModbusBitUtilities.extractUInt16(registers.getBytes(), 11) * 0.1f; + thing.setProperty("nominalOutputPower", String.valueOf(nominalOutputPower)); + + final int outputType = ModbusBitUtilities.extractUInt16(registers.getBytes(), 12); + String outputTypeString = mapOutputTypeValue(outputType); + thing.setProperty("outputType", outputTypeString); + }); + } + + private static String mapOutputTypeValue(int outputType) { + return switch (outputType) { + case 0 -> "Single Phase"; + case 1 -> "3P4L"; + case 2 -> "3P3L"; + default -> "unknown"; + }; + } + + 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(d -> d.toBigDecimal().multiply(channel.getMultiplier())).map(channel.getConversion()) + .map(bigDecimal -> new QuantityType<>(bigDecimal, channel.getUnit())) + .ifPresent(v -> updateState(createChannelUid(channel), v)); + } + }); + } + + private void readError(AsyncModbusFailure error) { + this.logger.error("Failed to get modbus data", error.getCause()); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Failed to retrieve data: " + error.getCause().getMessage()); + } + + private ChannelUID createChannelUid(SungrowInverterRegisters channel) { + return new ChannelUID(thing.getUID(), channel.name().toLowerCase()); + } +} diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java new file mode 100644 index 0000000000000..f0bc05ed3b921 --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java @@ -0,0 +1,172 @@ +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.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.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.unit.Units; + +/** + * 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. + DAILY_OUTPUT_ENERGY(5003, UINT16, 0.1f, Units.KILOWATT_HOUR), + TOTAL_OUTPUT_ENERGY(5004, UINT32_SWAP, 0.1f, Units.KILOWATT_HOUR), + INTERNAL_TEMPERATURE(5008, INT16, 0.1f, Units.KELVIN, ConversionConstants.CELSIUS_TO_KELVIN), + MPPT1_VOLTAGE(5011, UINT16, 0.1f, Units.VOLT), + MPPT1_CURRENT(5012, UINT16, 0.1f, Units.AMPERE), + MPPT2_VOLTAGE(5013, UINT16, 0.1f, Units.VOLT), + MPPT2_CURRENT(5014, UINT16, 0.1f, Units.AMPERE), + TOTAL_DC_POWER(5017, UINT32_SWAP, 1, Units.WATT), + PHASE_A_VOLTAGE(5019, UINT16, 0.1f, Units.VOLT), + PHASE_B_VOLTAGE(5020, UINT16, 0.1f, Units.VOLT), + PHASE_C_VOLTAGE(5021, UINT16, 0.1f, Units.VOLT), + + REACTIVE_POWER(5033, INT32_SWAP, 1, Units.VAR), + POWER_FACTOR(5035, INT16, 0.001f, Units.ONE), + GRID_FREQUENCY(5036, UINT16, 0.01f, Units.HERTZ), + + /** + * Not working + * EXPORT_LIMIT_MIN(10, 5622, UINT16, Units.WATT), + * EXPORT_LIMIT_MAX(10, 5623, UINT16, Units.WATT), + * BDC_RATED_POWER(100, 5628, UINT16, Units.WATT), + * MAX_CHARGING_CURRENT(1, 5635, UINT16, Units.AMPERE), + * MAX_DISCHARGING_CURRENT(1, 5636, UINT16, Units.AMPERE), + * PV_POWER_TODAY(1, 6100, UINT16, Units.WATT), + * DAILY_PV_ENERGY_YIELDS(1, 6196, UINT16, Units.KILOWATT_HOUR), + * MONTHLY_PV_ENERGY_YIELDS(1, 9227, UINT16, Units.KILOWATT_HOUR), + */ + + SYSTEM_STATE(13000, UINT16, 1, Units.ONE), + RUNNING_STATE(13001, UINT16, 1, Units.ONE), + DAILY_PV_GENERATION(13002, UINT16, 0.1f, Units.KILOWATT_HOUR), + TOTAL_PV_GENERATION(13003, UINT32_SWAP, 0.1f, Units.KILOWATT_HOUR), + DAILY_EXPORT_POWER_FROM_PV(13005, UINT16, 100, Units.WATT), + TOTAL_EXPORT_ENERGY_FROM_PV(13006, UINT32_SWAP, 0.1f, Units.KILOWATT_HOUR), + LOAD_POWER(13008, INT32_SWAP, 1, Units.WATT), + EXPORT_POWER(13010, INT32_SWAP, 1, Units.WATT), + DAILY_BATTERY_CHARGE(13012, UINT16, 0.1f, Units.KILOWATT_HOUR), + TOTAL_BATTERY_CHARGE(13013, UINT32_SWAP, 0.1f, Units.KILOWATT_HOUR), + CO2_REDUCTION(13015, UINT32_SWAP, 0.1f, tech.units.indriya.unit.Units.KILOGRAM), + DAILY_DIRECT_ENERGY_CONSUMPTION(13017, UINT16, 0.1f, Units.KILOWATT_HOUR), + TOTAL_DIRECT_ENERGY_CONSUMPTION(13018, UINT32_SWAP, 0.1f, Units.KILOWATT_HOUR), + BATTERY_VOLTAGE(13020, UINT16, 0.1f, Units.VOLT), + BATTERY_CURRENT(13021, UINT16, 0.1f, Units.AMPERE), + BATTERY_POWER(13022, UINT16, 1, Units.WATT), + BATTERY_LEVEL(13023, UINT16, 0.1f, Units.PERCENT), + BATTERY_HEALTHY(13024, UINT16, 0.1f, Units.PERCENT), + BATTERY_TEMPERATUR(13025, INT16, 0.1f, Units.KELVIN, ConversionConstants.CELSIUS_TO_KELVIN), + DAILY_BATTERY_DISCHARGE_ENERGY(13026, UINT16, 0.1f, Units.KILOWATT_HOUR), + TOTAL_BATTERY_DISCHARGE_ENERGY(13027, UINT32_SWAP, 0.1f, Units.KILOWATT_HOUR), + SELF_CONSUMPTION_TODAY(13029, UINT16, 0.1f, Units.PERCENT), + GRID_STATE(13030, UINT16, 1, Units.ONE), + PHASE_A_CURRENT(13031, INT16, 0.1f, Units.AMPERE), + PHASE_B_CURRENT(13032, INT16, 0.1f, Units.AMPERE), + PHASE_C_CURRENT(13033, INT16, 0.1f, Units.AMPERE), + TOTAL_ACTIVE_POWER(13034, INT32_SWAP, 1, Units.WATT), + DAILY_IMPORT_ENERGY(13036, UINT16, 0.1f, Units.KILOWATT_HOUR), + TOTAL_IMPORT_ENERGY(13037, UINT32_SWAP, 0.1f, Units.KILOWATT_HOUR), + BATTERY_CAPACITY(13039, UINT16, 0.1f, Units.KILOWATT_HOUR), + DAILY_CHARGE_ENERGY(13040, UINT16, 0.1f, Units.KILOWATT_HOUR), + TOTAL_CHARGE_ENERGY(13041, UINT32_SWAP, 0.1f, Units.KILOWATT_HOUR), + DRM_STATE(13043, UINT16, 1, Units.ONE), + + DAILY_EXPORT_ENERGY(13045, UINT16, 0.1f, Units.KILOWATT_HOUR), + TOTAL_EXPORT_ENERGY(13046, UINT32_SWAP, 0.1f, Units.KILOWATT_HOUR), + + INVERTER_ALARM(13050, UINT32_SWAP, 1, Units.ONE), + GRID_SIDE_FAULT(13052, UINT32_SWAP, 1, Units.ONE), + SYSTEM_FAULT_1(13054, UINT32_SWAP, 1, Units.ONE), + SYSTEM_FAULT_2(13056, UINT32_SWAP, 1, Units.ONE), + DC_SIDE_FAULT(13058, UINT32_SWAP, 1, Units.ONE), + PERMANENT_FAULT(13060, UINT32_SWAP, 1, Units.ONE), + BDC_SIDE_FAULT(13062, UINT32_SWAP, 1, Units.ONE), + BDC_SIDE_PERMANENT_FAULT(13064, UINT32_SWAP, 1, Units.ONE), + BATTERY_FAULT(13066, UINT32_SWAP, 1, Units.ONE), + BATTERY_ALARM(13068, UINT32_SWAP, 1, Units.ONE), + BMS_ALARM_1(13070, UINT32_SWAP, 1, Units.ONE), + BMS_PROTECTION(13072, UINT32_SWAP, 1, Units.ONE), + BMS_FAULT_1(13074, UINT32_SWAP, 1, Units.ONE), + BMS_FAULT_2(13076, UINT32_SWAP, 1, Units.ONE), + BMS_ALARM_2(13078, UINT32_SWAP, 1, Units.ONE); + + private final BigDecimal multiplier; + private final int registerNumber; + private final ValueType type; + private final Unit unit; + + private final Function conversion; + + SungrowInverterRegisters(int registerNumber, ValueType type, float multiplier, Unit unit, + Function conversion) { + this.multiplier = new BigDecimal(multiplier); + this.registerNumber = registerNumber; + this.type = type; + this.unit = unit; + this.conversion = conversion; + } + + private SungrowInverterRegisters(int registerNumber, ValueType type, float multiplier, Unit unit) { + this.multiplier = new BigDecimal(multiplier); + this.registerNumber = registerNumber; + this.type = type; + this.unit = unit; + this.conversion = Function.identity(); + } + + public Unit getUnit() { + return unit; + } + + public BigDecimal getMultiplier() { + return multiplier; + } + + public int getRegisterNumber() { + return registerNumber; + } + + public ValueType getType() { + return type; + } + + /** + * Returns the count of registers read to return the value of this register. + */ + public int getRegisterCount() { + return this.type.getBits() / 16; + } + + /** + * Returns the value conversion. + */ + public Function getConversion() { + return conversion; + } +} diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersBackup.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersBackup.java new file mode 100644 index 0000000000000..17fa732f61693 --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersBackup.java @@ -0,0 +1,120 @@ +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.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; +import static org.openhab.core.io.transport.modbus.ModbusConstants.ValueType.UINT16; +import static org.openhab.core.io.transport.modbus.ModbusConstants.ValueType.UINT32; + +import java.math.BigDecimal; + +import javax.measure.Unit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.io.transport.modbus.ModbusConstants.ValueType; +import org.openhab.core.library.unit.Units; + +/** + * The {@link SungrowInverterRegistersBackup} is responsible for defining Modbus registers and their units. + * + * @author Sönke Küper - Initial contribution + */ +@NonNullByDefault +public enum SungrowInverterRegistersBackup { + + // the following register numbers are 1-based. They need to be converted before sending them on the wire. + NOMINAL_ACTIVE_POWER(100, 5001, UINT16, Units.WATT), + DAILY_POWER_YIELDS(0.1f, 5003, UINT16, Units.KILOWATT_HOUR), + TOTAL_POWER_YIELDS(1, 5004, UINT32, Units.KILOWATT_HOUR), + TOTAL_RUNNING_TIME(0.1f, 5006, UINT32, Units.HOUR), + INTERNAL_TEMPERATURE(0.1f, 5008, INT16, Units.KELVIN), // TODO: Umrechnung C in K + 273,15 + TOTAL_APPARENT_POWER(1, 5009, UINT32, Units.VOLT_AMPERE), + MPPT1_VOLTAGE(0.1f, 5011, UINT16, Units.VOLT), + MPPT1_CURRENT(0.1f, 5012, UINT16, Units.AMPERE), + MPPT2_VOLTAGE(0.1f, 5013, UINT16, Units.VOLT), + MPPT2_CURRENT(0.1f, 5014, UINT16, Units.AMPERE), + MPPT3_VOLTAGE(0.1f, 5015, UINT16, Units.VOLT), + MPPT3_CURRENT(0.1f, 5016, UINT16, Units.AMPERE), + TOTAL_DC_POWER(1, 5017, UINT32, Units.WATT), + PHASE_A_VOLTAGE(0.1f, 5019, UINT16, Units.VOLT), + PHASE_B_VOLTAGE(0.1f, 5020, UINT16, Units.VOLT), + PHASE_C_VOLTAGE(0.1f, 5021, UINT16, Units.VOLT), + PHASE_A_CURRENT(0.1f, 5022, UINT16, Units.AMPERE), + PHASE_B_CURRENT(0.1f, 5023, UINT16, Units.AMPERE), + PHASE_C_CURRENT(0.1f, 5024, UINT16, Units.AMPERE), + TOTAL_ACTIVE_POWER(1, 5031, UINT32, Units.WATT), + TOTAL_REACTIVE_POWER(1, 5033, INT32, Units.VAR), + POWER_FACTOR(0.001f, 5035, INT16, Units.ONE), + GRID_FREQUENCY(0.1f, 5036, UINT16, Units.HERTZ), + WORK_STATE_1(1, 5038, UINT16, Units.ONE), + NOMINAL_REACTIVE_POWER(0.1f, 5049, UINT16, Units.KILOVAR), + ARRAY_INSULATION_RESISTANCE(1000, 5071, UINT16, Units.OHM), + ACTIVE_POWER_REGULATION_SETPOINT(1, 5077, UINT32, Units.WATT), + REACTIVE_POWER_REGULATION_SETPOINT(1, 5079, INT32, Units.VAR), + WORK_STATE_2(1, 5081, UINT32, Units.ONE), + METER_POWER(1, 5083, INT32, Units.WATT), + METER_PHASE_A_POWER(1, 5085, INT32, Units.WATT), + METER_PHASE_B_POWER(1, 5087, INT32, Units.WATT), + METER_PHASE_C_POWER(1, 5089, INT32, Units.WATT), + LOAD_POWER(1, 5091, INT32, Units.WATT), + DAILY_EXPORT_ENERGY(0.1f, 5093, UINT32, Units.KILOWATT_HOUR), + TOTAL_EXPORT_ENERGY(0.1f, 5095, UINT32, Units.KILOWATT_HOUR), + DAILY_IMPORT_ENERGY(0.1f, 5097, UINT32, Units.KILOWATT_HOUR), + TOTAL_IMPORT_ENERGY(0.1f, 5099, UINT32, Units.KILOWATT_HOUR), + DAILY_DIRECT_ENERGY_CONSUMPTION(0.1f, 5101, UINT32, Units.KILOWATT_HOUR), + TOTAL_DIRECT_ENERGY_CONSUMPTION(0.1f, 5103, UINT32, Units.KILOWATT_HOUR), + DAILY_RUNNING_TIME(1, 5113, UINT16, Units.MINUTE), + MONTHLY_POWER_YIELDS(0.1f, 5128, UINT32, Units.KILOWATT_HOUR), + TOTAL_POWER_YIELDS_FINE(0.1f, 5144, UINT32, Units.KILOWATT_HOUR), + NEGATIVE_VOLTAGE_TO_GROUND(0.1f, 5146, INT16, Units.VOLT), + BUS_VOLTAGE(0.1f, 5147, UINT16, Units.VOLT), + GRID_FREQUENCY_FINE(0.01f, 5148, UINT16, Units.HERTZ), + PID_WORK_STATE(1, 5150, UINT16, Units.ONE), + PID_ALARM_CODE(1, 5151, UINT16, Units.ONE); + + private final BigDecimal multiplier; + private final int registerNumber; + private final ValueType type; + private final Unit unit; + + private SungrowInverterRegistersBackup(float multiplier, int registerNumber, ValueType type, Unit unit) { + this.multiplier = new BigDecimal(multiplier); + this.registerNumber = registerNumber; + this.type = type; + this.unit = unit; + } + + public Unit getUnit() { + return unit; + } + + public BigDecimal getMultiplier() { + return multiplier; + } + + public int getRegisterNumber() { + return registerNumber; + } + + public ValueType getType() { + return type; + } + + /** + * Returns the count of registers read to return the value of this register. + */ + public int getRegisterCount() { + return this.type.getBits() / 16; + } +} diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SystemState.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SystemState.java new file mode 100644 index 0000000000000..7bd7084691b1d --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SystemState.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.modbus.sungrow.internal; + +import java.util.Arrays; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Definition of Sungrow system state codes. + * + * @author Sönke Küper - Initial contribution + */ +@NonNullByDefault +public enum SystemState { + + STOP("Stop", 0x0002), + STANDBY("Standby", 0x0008), + INIT_STANDBY("Initial standby", 0x0010), + STARTUP("Startup", 0x0020), + RUNNING("Running", 0x0040), + FAULT("Fault", 0x0100), + MAINTAIN_MODE("Running in maintain mode", 0x0400), + FORCED_MODE("Running in forced mode", 0x0800), + OFF_GRID("Running in off-grid mode", 0x1000), + RESTARTING("Restarting", 0x2501), + EXTERNAL_EMS("Running in External EMS mode", 0x4000); + + private static final Map CODE_MAPPING = initMapping(); + + private static Map initMapping() { + return Arrays.stream(SystemState.values()) + .collect(Collectors.toMap(SystemState::getStateCode, Function.identity())); + } + + /** + * Returns the {@link SystemState} for the given state code. + */ + @Nullable + public static SystemState getByStateCode(int stateCode) { + return CODE_MAPPING.get(stateCode); + } + + private final int stateCode; + private final String description; + + SystemState(String description, int stateCode) { + this.stateCode = stateCode; + this.description = description; + } + + public int getStateCode() { + return stateCode; + } + + public String getDescription() { + return description; + } +} diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/addon/addon.xml deleted file mode 100644 index 4bc7f3f038e93..0000000000000 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/addon/addon.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - binding - Modbus Sungrow Binding - This is the binding for Modbus Sungrow. - - diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/config/config.xml new file mode 100644 index 0000000000000..10a87cdb03014 --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/config/config.xml @@ -0,0 +1,15 @@ + + + + + + + Time between polling the data in ms. + 5000 + + + + diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/i18n/sungrow.properties b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/i18n/sungrow.properties index 0c2f44015a92d..15bba7b7c6af2 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/i18n/sungrow.properties +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/i18n/sungrow.properties @@ -1,3 +1,76 @@ -# FIXME: please add all English translations to this file so the texts can be translated using Crowdin -# FIXME: to generate the content of this file run: mvn i18n:generate-default-translations -# FIXME: see also: https://www.openhab.org/docs/developer/utils/i18n.html +# 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 types + +channel-type.modbus.battery_alarm.label = BATTERY_ALARM +channel-type.modbus.battery_capacity.label = Battery Capacity +channel-type.modbus.battery_current.label = Battery Current +channel-type.modbus.battery_fault.label = BATTERY_FAULT +channel-type.modbus.battery_healthy.label = Battery Health (SOH) +channel-type.modbus.battery_level.label = Battery Level (SOC) +channel-type.modbus.battery_power.label = BATTERY_POWER +channel-type.modbus.battery_temperatur.label = Battery Temperature +channel-type.modbus.battery_voltage.label = Battery Voltage +channel-type.modbus.bdc_side_fault.label = BDC_SIDE_FAULT +channel-type.modbus.bdc_side_permanent_fault.label = BDC_SIDE_PERMANENT_FAULT +channel-type.modbus.bms_alarm_1.label = BMS_ALARM_1 +channel-type.modbus.bms_alarm_2.label = BMS_ALARM_2 +channel-type.modbus.bms_fault_1.label = BMS_FAULT_1 +channel-type.modbus.bms_fault_2.label = BMS_FAULT_2 +channel-type.modbus.bms_protection.label = BMS_PROTECTION +channel-type.modbus.co2_reduction.label = Co2 Reduction +channel-type.modbus.daily_battery_charge.label = Daily Battery Charging Energy from PV +channel-type.modbus.daily_battery_discharge_energy.label = Daily Battery Discharging Energy +channel-type.modbus.daily_charge_energy.label = Daily Battery Charging Energy +channel-type.modbus.daily_direct_energy_consumption.label = Daily Load Energy Consumption from PV +channel-type.modbus.daily_export_energy.label = Daily Feed-in Energy +channel-type.modbus.daily_export_power_from_pv.label = Daily Feed-in Energy (PV) +channel-type.modbus.daily_import_energy.label = Daily Purchased Energy +channel-type.modbus.daily_output_energy.label = DAILY_OUTPUT_ENERGY +channel-type.modbus.daily_pv_generation.label = Daily PV Yield +channel-type.modbus.dc_side_fault.label = DC_SIDE_FAULT +channel-type.modbus.drm_state.label = DRM_STATE +channel-type.modbus.export_power.label = Total Export Active Power +channel-type.modbus.grid_frequency.label = Grid Frequency +channel-type.modbus.grid_side_fault.label = GRID_SIDE_FAULT +channel-type.modbus.grid_state.label = GRID_STATE +channel-type.modbus.internal_temperature.label = Internal Temperature +channel-type.modbus.inverter_alarm.label = INVERTER_ALARM +channel-type.modbus.load_power.label = Load Power +channel-type.modbus.mppt1_current.label = MPPT1 Current +channel-type.modbus.mppt1_voltage.label = MPPT1 Voltage +channel-type.modbus.mppt2_current.label = MPPT2 Current +channel-type.modbus.mppt2_voltage.label = MPPT2 Voltage +channel-type.modbus.permanent_fault.label = PERMANENT_FAULT +channel-type.modbus.phase_a_current.label = Phase A Current +channel-type.modbus.phase_a_voltage.label = Phase A Voltage +channel-type.modbus.phase_b_current.label = Phase B Current +channel-type.modbus.phase_b_voltage.label = Phase B Voltage +channel-type.modbus.phase_c_current.label = Phase C Current +channel-type.modbus.phase_c_voltage.label = Phase C Voltage +channel-type.modbus.power_factor.label = Total Power Factor +channel-type.modbus.reactive_power.label = Total Reactive Power +channel-type.modbus.running_state.label = Running Status +channel-type.modbus.self_consumption_today.label = Daily Self-consumption Rate +channel-type.modbus.system_fault_1.label = SYSTEM_FAULT_1 +channel-type.modbus.system_fault_2.label = SYSTEM_FAULT_2 +channel-type.modbus.system_state.label = Current Status +channel-type.modbus.total_active_power.label = Total Active Power +channel-type.modbus.total_battery_charge.label = Total Battery Charging Energy +channel-type.modbus.total_battery_discharge_energy.label = Total Battery Discharging Energy +channel-type.modbus.total_charge_energy.label = Total Battery Charging Energy from PV +channel-type.modbus.total_dc_power.label = Total DC Power +channel-type.modbus.total_direct_energy_consumption.label = Total Load Energy Consumption from PV +channel-type.modbus.total_export_energy.label = Total Feed-in Energy +channel-type.modbus.total_export_energy_from_pv.label = Total Feed-in Energy (PV) +channel-type.modbus.total_import_energy.label = Total Purchased Energy +channel-type.modbus.total_output_energy.label = TOTAL_OUTPUT_ENERGY +channel-type.modbus.total_pv_generation.label = Total PV Yield diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/i18n/sungrow_de.properties b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/i18n/sungrow_de.properties new file mode 100644 index 0000000000000..8170b6483f50d --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/i18n/sungrow_de.properties @@ -0,0 +1,76 @@ +# 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 types + +channel-type.modbus.battery_alarm.label = BATTERY_ALARM +channel-type.modbus.battery_capacity.label = Batteriekapazität +channel-type.modbus.battery_current.label = Batteriestrom +channel-type.modbus.battery_fault.label = BATTERY_FAULT +channel-type.modbus.battery_healthy.label = Gesundheit der Batterie (SOH) +channel-type.modbus.battery_level.label = Batteriestand +channel-type.modbus.battery_power.label = BATTERY_POWER +channel-type.modbus.battery_temperatur.label = Batterietemperatur +channel-type.modbus.battery_voltage.label = Batteriespannung +channel-type.modbus.bdc_side_fault.label = BDC_SIDE_FAULT +channel-type.modbus.bdc_side_permanent_fault.label = BDC_SIDE_PERMANENT_FAULT +channel-type.modbus.bms_alarm_1.label = BMS_ALARM_1 +channel-type.modbus.bms_alarm_2.label = BMS_ALARM_2 +channel-type.modbus.bms_fault_1.label = BMS_FAULT_1 +channel-type.modbus.bms_fault_2.label = BMS_FAULT_2 +channel-type.modbus.bms_protection.label = BMS_PROTECTION +channel-type.modbus.co2_reduction.label = Co2 Einsparung +channel-type.modbus.daily_battery_charge.label = Eingespeicherte PV-Energie (Tagesbasis) +channel-type.modbus.daily_battery_discharge_energy.label = Aus Batterie entnommen (Tagesbasis) +channel-type.modbus.daily_charge_energy.label = In Batterie gespeichert (Tagesbasis) +channel-type.modbus.daily_direct_energy_consumption.label = Tages-PV-Eigenverbrauch +channel-type.modbus.daily_export_energy.label = Tägliche Netzeinspeisung +channel-type.modbus.daily_export_power_from_pv.label = Tägliches PV-Einspeisenetzvolumen +channel-type.modbus.daily_import_energy.label = Tägliche Energie abgenommen vom Netz +channel-type.modbus.daily_output_energy.label = DAILY_OUTPUT_ENERGY +channel-type.modbus.daily_pv_generation.label = Tägliche PV-Stromerzeugung +channel-type.modbus.dc_side_fault.label = DC_SIDE_FAULT +channel-type.modbus.drm_state.label = DRM_STATE +channel-type.modbus.export_power.label = Wirkleistung Einspeisung +channel-type.modbus.grid_frequency.label = Netzfrequenz +channel-type.modbus.grid_side_fault.label = GRID_SIDE_FAULT +channel-type.modbus.grid_state.label = GRID_STATE +channel-type.modbus.internal_temperature.label = Temperatur +channel-type.modbus.inverter_alarm.label = INVERTER_ALARM +channel-type.modbus.load_power.label = Lastleistung +channel-type.modbus.mppt1_current.label = MPPT1 Strom +channel-type.modbus.mppt1_voltage.label = MPPT1 Spannung +channel-type.modbus.mppt2_current.label = MPPT2 Strom +channel-type.modbus.mppt2_voltage.label = MPPT2 Spannung +channel-type.modbus.permanent_fault.label = PERMANENT_FAULT +channel-type.modbus.phase_a_current.label = Strom Phase A +channel-type.modbus.phase_a_voltage.label = Spannung Phase A +channel-type.modbus.phase_b_current.label = Strom Phase B +channel-type.modbus.phase_b_voltage.label = Spannung Phase B +channel-type.modbus.phase_c_current.label = Strom Phase C +channel-type.modbus.phase_c_voltage.label = Spannung Phase C +channel-type.modbus.power_factor.label = Leistungsfaktor +channel-type.modbus.reactive_power.label = Blindleistung +channel-type.modbus.running_state.label = Betriebsstatus +channel-type.modbus.self_consumption_today.label = Tägliche Eigenverbrauchsrate +channel-type.modbus.system_fault_1.label = SYSTEM_FAULT_1 +channel-type.modbus.system_fault_2.label = SYSTEM_FAULT_2 +channel-type.modbus.system_state.label = Aktueller Zustand +channel-type.modbus.total_active_power.label = Wirkleistung Gesamt +channel-type.modbus.total_battery_charge.label = Eingespeicherte PV-Energie (Gesamt) +channel-type.modbus.total_battery_discharge_energy.label = Aus Batterie entnommen (Gesamt) +channel-type.modbus.total_charge_energy.label = In Batterie gespeichert (Gesamt) +channel-type.modbus.total_dc_power.label = PV-Leistung +channel-type.modbus.total_direct_energy_consumption.label = Gesamter PV-Eigenverbrauch +channel-type.modbus.total_export_energy.label = Netzeinspeisung gesamt +channel-type.modbus.total_export_energy_from_pv.label = Gesamt-Verlauf PV Einspeisemenge +channel-type.modbus.total_import_energy.label = Netzbezug insgesamt +channel-type.modbus.total_output_energy.label = TOTAL_OUTPUT_ENERGY +channel-type.modbus.total_pv_generation.label = Gesamte PV-Stromerzeugung diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml index a6aa2b5d12bf3..e388514fd25cd 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml @@ -1,48 +1,405 @@ - - - - - - - Sample thing for Modbus Sungrow Binding + + + + + + + Sungrow inverter connected via Modbus. - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - network-address - - Hostname or IP address of the device - - - password - - Password to access the device - - - - Interval the device is polled in sec. - 600 - true - - + - - + + Number:Energy + + + + + Number:Energy + + + + Number:Temperature - - Sample channel for ModbusSungrow Binding + + + + + Number:ElectricPotential + + + + + Number:ElectricCurrent + + + + + Number:ElectricPotential + + + + + Number:ElectricCurrent + + + + + Number:Power + + + + + Number:ElectricPotential + + + + + Number:ElectricPotential + + + + + Number:ElectricPotential + + + + + Number:Power + + + + + Number:Dimensionless + + + + + Number:Frequency + + + + + Number:Dimensionless + + + + + Number:Energy + + + + Number:Energy + + + + + Number:Energy + + + + + Number:Power + + + + + Number:Energy + + + + + Number:Power + + + + + Number:Power + + + + + Number:Energy + + + + + Number:Energy + + + + + Number:Mass + + + + + Number:Energy + + + + + Number:Energy + + + + + Number:ElectricPotential + + + + + Number:ElectricCurrent + + + + + Number:Power + + + + + Number:Dimensionless + + + + + Number:Dimensionless + + + + + Number:Temperature + + + + + Number:Energy + + + + + Number:Energy + + + + + Number:Dimensionless + + + + + Number:Dimensionless + + + + + Number:ElectricCurrent + + + + + Number:ElectricCurrent + + + + + Number:ElectricCurrent + + + + + Number:Power + + + + + Number:Energy + + + + + Number:Energy + + + + + Number:Energy + + + + + Number:Energy + + + + + Number:Energy + + + + + Number:Dimensionless + + + + + Number:Energy + + + + + Number:Energy + + + + + Number:Dimensionless + + + + + Number:Dimensionless + + + + + Number:Dimensionless + + + + + Number:Dimensionless + + + + + Number:Dimensionless + + + + + Number:Dimensionless + + + + + Number:Dimensionless + + + + + Number:Dimensionless + + + + + Number:Dimensionless + + + + + Number:Dimensionless + + + + + Number:Dimensionless + + + + + Number:Dimensionless + + + + + Number:Dimensionless + + + + + Number:Dimensionless + + + + + Number:Dimensionless + + + + diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/test/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersTest.java b/bundles/org.openhab.binding.modbus.sungrow/src/test/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersTest.java new file mode 100644 index 0000000000000..09da71ed491b7 --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sungrow/src/test/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersTest.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.modbus.sungrow.internal; + +import org.junit.jupiter.api.Test; + +/** + * Tests + * + * @author Sönke Küper - Initial contribution + */ +class SungrowInverterRegistersTest { + + @Test + public void test() { + for (SungrowInverterRegisters register : SungrowInverterRegisters.values()) { + System.out.printf("\t\n", register.name().toLowerCase(), + register.name().toLowerCase()); + + } + + for (SungrowInverterRegisters register : SungrowInverterRegisters.values()) { + System.out.printf( + "\t\n" + "\t\t%s\n" + "\t\t\n" + + "\t\t\n" + "\t\n", + register.name().toLowerCase(), register.getUnit(), register.name()); + + } + } +} From cc12906d47d299d94868c166245b59550572aaf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20K=C3=BCper?= Date: Mon, 18 Dec 2023 21:15:07 +0100 Subject: [PATCH 03/30] 0000: Reworked registers and channels, due some registers are not working. Added categories and tags for channels. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- .../sungrow/internal/DeviceTypeCode.java | 78 --- .../internal/DeviceTypeCodeBackup.java | 132 ----- .../internal/SungrowInverterHandler.java | 56 +- .../internal/SungrowInverterRegisters.java | 218 ++++--- .../SungrowInverterRegistersBackup.java | 120 ---- .../modbus/sungrow/internal/SystemState.java | 73 --- .../resources/OH-INF/i18n/sungrow.properties | 116 ++-- .../OH-INF/i18n/sungrow_de.properties | 116 ++-- .../resources/OH-INF/thing/thing-types.xml | 561 +++++++++++------- .../SungrowInverterRegistersTest.java | 40 -- 10 files changed, 579 insertions(+), 931 deletions(-) delete mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/DeviceTypeCode.java delete mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/DeviceTypeCodeBackup.java delete mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersBackup.java delete mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SystemState.java delete mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/test/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersTest.java diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/DeviceTypeCode.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/DeviceTypeCode.java deleted file mode 100644 index a4da1e4bdf15f..0000000000000 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/DeviceTypeCode.java +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (c) 2010-2023 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.modbus.sungrow.internal; - -import java.util.Arrays; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; - -/** - * Definition of Sungrow Device codes. - * - * @author Sönke Küper - Initial contribution - */ -@NonNullByDefault -public enum DeviceTypeCode { - - SH3K6("SH3K6", 0xD06), - SH4K6("SH4K6", 0xD07), - SH5K_20("SH5K-20", 0xD09), - SH5K_V13("SH5K-V13", 0xD03), - SH3K6_30("SH3K6-30", 0xD0A), - SH4K6_30("SH4K6-30", 0xD0B), - SH5K_30("SH5K-30", 0xD0C), - SH3_0RS("SH3.0RS", 0xD17), - SH3_6RS("SH3.6RS", 0xD0D), - SH4_0RS("SH4.0RS", 0xD18), - SH5_0RS("SH5.0RS", 0xD0F), - SH6_0RS("SH6.0RS", 0xD10), - SH5_0RT("SH5.0RT", 0xE00), - SH6_0RT("SH6.0RT", 0xE01), - SH8_0RT("SH8.0RT", 0xE02), - SH10RT("SH10RT", 0xE03); - - private static final Map CODE_MAPPING = initMapping(); - - private static Map initMapping() { - return Arrays.stream(DeviceTypeCode.values()) - .collect(Collectors.toMap(DeviceTypeCode::getTypeCode, Function.identity())); - } - - /** - * Returns the DeviceTypeCodes for the given type code. - */ - @Nullable - public static DeviceTypeCode getByTypeCode(int typeCode) { - return CODE_MAPPING.get(typeCode); - } - - private final int typeCode; - private final String model; - - DeviceTypeCode(String model, int typeCode) { - this.typeCode = typeCode; - this.model = model; - } - - public int getTypeCode() { - return typeCode; - } - - public String getModel() { - return model; - } -} diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/DeviceTypeCodeBackup.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/DeviceTypeCodeBackup.java deleted file mode 100644 index fbc4fb70b3d1e..0000000000000 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/DeviceTypeCodeBackup.java +++ /dev/null @@ -1,132 +0,0 @@ -/** - * Copyright (c) 2010-2023 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.modbus.sungrow.internal; - -import java.util.Arrays; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; - -/** - * Definition of Sungrow Device codes. - * - * @author Sönke Küper - Initial contribution - */ -@NonNullByDefault -public enum DeviceTypeCodeBackup { - - SG30KTL(0x27, "SG30KTL", 2), - SG10KTL(0x26, "SG10KTL", 2), - SG12KTL(0x29, "SG12KTL", 2), - SG15KTL(0x28, "SG15KTL", 2), - SG20KTL(0x2A, "SG20KTL", 2), - SG30KU(0x2C, "SG30KU", 2), - SG36KTL(0x2D, "SG36KTL", 2), - SG36KU(0x2E, "SG36KU", 2), - SG40KTL(0x2F, "SG40KTL", 2), - SG40KTL_M(0x0135, "SG40KTL-M", 3), - SG50KTL_M(0x011B, "SG50KTL-M", 4), - SG60KTL_M(0x0131, "SG60KTL-M", 4), - SG60KU(0x0136, "SG60KU", 1), - SG30KTL_M(0x0141, "SG30KTL-M", 3), - SG30KTL_M_V31(0x70, "SG30KTL-M-V31", 3), - SG33KTL_M(0x0134, "SG33KTL-M", 3), - SG36KTL_M(0x74, "SG36KTL-M", 3), - SG33K3J(0x013D, "SG33K3J", 3), - SG49K5J(0x0137, "SG49K5J", 4), - SG34KJ(0x72, "SG34KJ", 2), - LP_P34KSG(0x73, "LP_P34KSG", 1), - // Duplicate typeCode: SG50KTL_M_20(0x011B, "SG50KTL-M-20", 4), - SG60KTL(0x010F, "SG60KTL", 1), - SG80KTL(0x0138, "SG80KTL", 1), - // Duplicate typeCode: SG80KTL_20(0x0138, "SG80KTL-20", 1), - SG60KU_M(0x0132, "SG60KU-M", 4), - SG5KTL_MT(0x0147, "SG5KTL-MT", 2), - SG6KTL_MT(0x0148, "SG6KTL-MT", 2), - SG8KTL_M(0x013F, "SG8KTL-M", 2), - SG10KTL_M(0x013E, "SG10KTL-M", 2), - SG10KTL_MT(0x2C0F, "SG10KTL-MT", 2), - SG12KTL_M(0x013C, "SG12KTL-M", 2), - SG15KTL_M(0x0142, "SG15KTL-M", 2), - SG17KTL_M(0x0149, "SG17KTL-M", 2), - SG20KTL_M(0x0143, "SG20KTL-M", 2), - SG80KTL_M(0x0139, "SG80KTL-M", 4), - SG111HV(0x014C, "SG111HV", 1), - SG125HV(0x013B, "SG125HV", 1), - SG125HV_20(0x2C03, "SG125HV-20", 1), - SG30CX(0x2C10, "SG30CX", 3), - SG33CX(0x2C00, "SG33CX", 3), - SG36CX_US(0x2C0A, "SG36CX-US", 3), - SG40CX(0x2C01, "SG40CX", 4), - SG50CX(0x2C02, "SG50CX", 5), - SG60CX_US(0x2C0B, "SG60CX-US", 5), - SG110CX(0x2C06, "SG110CX", 9), - SG250HX(0x2C0C, "SG250HX", 1), - SG250HX_US(0x2C11, "SG250HX-US", 1), - SG100CX(0x2C12, "SG100CX", 1), - // SG100CX_JP(0x2C12, "SG100CX-JP", 1), - SG250HX_IN(0x2C13, "SG250HX-IN", 1), - SG25CX_SA(0x2C15, "SG25CX-SA", 3), - SG75CX(0x2C22, "SG75CX", 9), - SG3_0RT(0x243D, "SG3.0RT", 2), - SG4_0RT(0x243E, "SG4.0RT", 2), - SG5_0RT(0x2430, "SG5.0RT", 2), - SG6_0RT(0x2431, "SG6.0RT", 2), - SG7_0RT(0x243C, "SG7.0RT", 2), - SG8_0RT(0x2432, "SG8.0RT", 2), - SG10RT(0x2433, "SG10RT", 2), - SG12RT(0x2434, "SG12RT", 2), - SG15RT(0x2435, "SG15RT", 2), - SG17RT(0x2436, "SG17RT", 2), - SG20RT(0x2437, "SG20RT", 2); - - private static final Map CODE_MAPPING = initMapping(); - - private static Map initMapping() { - return Arrays.stream(DeviceTypeCodeBackup.values()) - .collect(Collectors.toMap(DeviceTypeCodeBackup::getTypeCode, Function.identity())); - } - - /** - * Returns the DeviceTypeCodes for the given type code. - */ - @Nullable - public static DeviceTypeCodeBackup getByTypeCode(int typeCode) { - return CODE_MAPPING.get(typeCode); - } - - private final int typeCode; - private final String model; - private final int mpptCount; - - DeviceTypeCodeBackup(int typeCode, String model, int mpptCount) { - this.typeCode = typeCode; - this.model = model; - this.mpptCount = mpptCount; - } - - public int getMpptCount() { - return mpptCount; - } - - public int getTypeCode() { - return typeCode; - } - - public String getModel() { - return model; - } -} diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterHandler.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterHandler.java index d8f6b3bd45360..5494e7768e3cb 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterHandler.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterHandler.java @@ -12,7 +12,6 @@ */ package org.openhab.binding.modbus.sungrow.internal; -import java.nio.charset.StandardCharsets; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; @@ -26,7 +25,6 @@ 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.library.types.QuantityType; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingStatus; @@ -72,8 +70,6 @@ private ModbusReadRequestBlueprint initReadRequest(Deque { - final byte[] serialBytes = new byte[10]; - System.arraycopy(registers.getBytes(), 0, serialBytes, 0, 10); - String serialNumber = new String(serialBytes, StandardCharsets.UTF_8); - thing.setProperty("serialNumber", serialNumber); - - final int deviceTypeCode = ModbusBitUtilities.extractUInt16(registers.getBytes(), 10); - final DeviceTypeCode typeCode = DeviceTypeCode.getByTypeCode(deviceTypeCode); - if (typeCode == null) { - thing.setProperty("model", "unknown device type: " + deviceTypeCode); - } else { - thing.setProperty("model", typeCode.getModel()); - } - - final float nominalOutputPower = ModbusBitUtilities.extractUInt16(registers.getBytes(), 11) * 0.1f; - thing.setProperty("nominalOutputPower", String.valueOf(nominalOutputPower)); - - final int outputType = ModbusBitUtilities.extractUInt16(registers.getBytes(), 12); - String outputTypeString = mapOutputTypeValue(outputType); - thing.setProperty("outputType", outputTypeString); - }); - } - - private static String mapOutputTypeValue(int outputType) { - return switch (outputType) { - case 0 -> "Single Phase"; - case 1 -> "3P4L"; - case 2 -> "3P3L"; - default -> "unknown"; - }; - } - private void readSuccessful(ModbusRequest request, AsyncModbusReadResult result) { result.getRegisters().ifPresent(registers -> { if (getThing().getStatus() != ThingStatus.ONLINE) { @@ -208,9 +164,7 @@ private void readSuccessful(ModbusRequest request, AsyncModbusReadResult result) int index = channel.getRegisterNumber() - firstRegister; ModbusBitUtilities.extractStateFromRegisters(registers, index, channel.getType()) - .map(d -> d.toBigDecimal().multiply(channel.getMultiplier())).map(channel.getConversion()) - .map(bigDecimal -> new QuantityType<>(bigDecimal, channel.getUnit())) - .ifPresent(v -> updateState(createChannelUid(channel), v)); + .map(channel::createState).ifPresent(v -> updateState(createChannelUid(channel), v)); } }); } @@ -222,6 +176,10 @@ private void readError(AsyncModbusFailure error) { } private ChannelUID createChannelUid(SungrowInverterRegisters channel) { - return new ChannelUID(thing.getUID(), channel.name().toLowerCase()); + return new ChannelUID( // + thing.getUID(), // + "sg-" + channel.getChannelGroup(), // + "sg-" + channel.name().toLowerCase() // + ); } } diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java index f0bc05ed3b921..ced8db703de20 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java @@ -24,7 +24,11 @@ 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. @@ -35,117 +39,140 @@ public enum SungrowInverterRegisters { // the following register numbers are 1-based. They need to be converted before sending them on the wire. - DAILY_OUTPUT_ENERGY(5003, UINT16, 0.1f, Units.KILOWATT_HOUR), - TOTAL_OUTPUT_ENERGY(5004, UINT32_SWAP, 0.1f, Units.KILOWATT_HOUR), - INTERNAL_TEMPERATURE(5008, INT16, 0.1f, Units.KELVIN, ConversionConstants.CELSIUS_TO_KELVIN), - MPPT1_VOLTAGE(5011, UINT16, 0.1f, Units.VOLT), - MPPT1_CURRENT(5012, UINT16, 0.1f, Units.AMPERE), - MPPT2_VOLTAGE(5013, UINT16, 0.1f, Units.VOLT), - MPPT2_CURRENT(5014, UINT16, 0.1f, Units.AMPERE), - TOTAL_DC_POWER(5017, UINT32_SWAP, 1, Units.WATT), - PHASE_A_VOLTAGE(5019, UINT16, 0.1f, Units.VOLT), - PHASE_B_VOLTAGE(5020, UINT16, 0.1f, Units.VOLT), - PHASE_C_VOLTAGE(5021, UINT16, 0.1f, Units.VOLT), - - REACTIVE_POWER(5033, INT32_SWAP, 1, Units.VAR), - POWER_FACTOR(5035, INT16, 0.001f, Units.ONE), - GRID_FREQUENCY(5036, UINT16, 0.01f, Units.HERTZ), + // Registers are duplicate to DAILY_PV_GENERATION / TOTAL_PV_GENERATION + // DAILY_OUTPUT_ENERGY(5003, UINT16, 0.1f, quantityTypeFactory(Units.KILOWATT_HOUR), + // TOTAL_OUTPUT_ENERGY(5004, UINT32_SWAP, 0.1f, quantityTypeFactory(Units.KILOWATT_HOUR), + + INTERNAL_TEMPERATURE(5008, INT16, 0.1f, QUANTITY_FACTORY(Units.KELVIN), ConversionConstants.CELSIUS_TO_KELVIN, + "overview"), + MPPT1_VOLTAGE(5011, UINT16, 0.1f, QUANTITY_FACTORY(Units.VOLT), "mppt_information"), + MPPT1_CURRENT(5012, UINT16, 0.1f, QUANTITY_FACTORY(Units.AMPERE), "mppt_information"), + MPPT2_VOLTAGE(5013, UINT16, 0.1f, QUANTITY_FACTORY(Units.VOLT), "mppt_information"), + MPPT2_CURRENT(5014, UINT16, 0.1f, QUANTITY_FACTORY(Units.AMPERE), "mppt_information"), + TOTAL_DC_POWER(5017, UINT32_SWAP, 1, QUANTITY_FACTORY(Units.WATT), "overview"), + PHASE_A_VOLTAGE(5019, UINT16, 0.1f, QUANTITY_FACTORY(Units.VOLT), "overview"), + PHASE_B_VOLTAGE(5020, UINT16, 0.1f, QUANTITY_FACTORY(Units.VOLT), "overview"), + PHASE_C_VOLTAGE(5021, UINT16, 0.1f, QUANTITY_FACTORY(Units.VOLT), "overview"), + + REACTIVE_POWER(5033, INT32_SWAP, 1, QUANTITY_FACTORY(Units.VAR), "overview"), + POWER_FACTOR(5035, INT16, 0.001f, DecimalType::new, "overview"), + GRID_FREQUENCY(5036, UINT16, 0.01f, QUANTITY_FACTORY(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)), + */ /** - * Not working - * EXPORT_LIMIT_MIN(10, 5622, UINT16, Units.WATT), - * EXPORT_LIMIT_MAX(10, 5623, UINT16, Units.WATT), - * BDC_RATED_POWER(100, 5628, UINT16, Units.WATT), - * MAX_CHARGING_CURRENT(1, 5635, UINT16, Units.AMPERE), - * MAX_DISCHARGING_CURRENT(1, 5636, UINT16, Units.AMPERE), - * PV_POWER_TODAY(1, 6100, UINT16, Units.WATT), - * DAILY_PV_ENERGY_YIELDS(1, 6196, UINT16, Units.KILOWATT_HOUR), - * MONTHLY_PV_ENERGY_YIELDS(1, 9227, UINT16, Units.KILOWATT_HOUR), + * Registers return invalid values. + * SYSTEM_STATE(13000, UINT16, 1, quantityTypeFactory(Units.ONE)), + * RUNNING_STATE(13001, UINT16, 1, quantityTypeFactory(Units.ONE)), */ - SYSTEM_STATE(13000, UINT16, 1, Units.ONE), - RUNNING_STATE(13001, UINT16, 1, Units.ONE), - DAILY_PV_GENERATION(13002, UINT16, 0.1f, Units.KILOWATT_HOUR), - TOTAL_PV_GENERATION(13003, UINT32_SWAP, 0.1f, Units.KILOWATT_HOUR), - DAILY_EXPORT_POWER_FROM_PV(13005, UINT16, 100, Units.WATT), - TOTAL_EXPORT_ENERGY_FROM_PV(13006, UINT32_SWAP, 0.1f, Units.KILOWATT_HOUR), - LOAD_POWER(13008, INT32_SWAP, 1, Units.WATT), - EXPORT_POWER(13010, INT32_SWAP, 1, Units.WATT), - DAILY_BATTERY_CHARGE(13012, UINT16, 0.1f, Units.KILOWATT_HOUR), - TOTAL_BATTERY_CHARGE(13013, UINT32_SWAP, 0.1f, Units.KILOWATT_HOUR), - CO2_REDUCTION(13015, UINT32_SWAP, 0.1f, tech.units.indriya.unit.Units.KILOGRAM), - DAILY_DIRECT_ENERGY_CONSUMPTION(13017, UINT16, 0.1f, Units.KILOWATT_HOUR), - TOTAL_DIRECT_ENERGY_CONSUMPTION(13018, UINT32_SWAP, 0.1f, Units.KILOWATT_HOUR), - BATTERY_VOLTAGE(13020, UINT16, 0.1f, Units.VOLT), - BATTERY_CURRENT(13021, UINT16, 0.1f, Units.AMPERE), - BATTERY_POWER(13022, UINT16, 1, Units.WATT), - BATTERY_LEVEL(13023, UINT16, 0.1f, Units.PERCENT), - BATTERY_HEALTHY(13024, UINT16, 0.1f, Units.PERCENT), - BATTERY_TEMPERATUR(13025, INT16, 0.1f, Units.KELVIN, ConversionConstants.CELSIUS_TO_KELVIN), - DAILY_BATTERY_DISCHARGE_ENERGY(13026, UINT16, 0.1f, Units.KILOWATT_HOUR), - TOTAL_BATTERY_DISCHARGE_ENERGY(13027, UINT32_SWAP, 0.1f, Units.KILOWATT_HOUR), - SELF_CONSUMPTION_TODAY(13029, UINT16, 0.1f, Units.PERCENT), - GRID_STATE(13030, UINT16, 1, Units.ONE), - PHASE_A_CURRENT(13031, INT16, 0.1f, Units.AMPERE), - PHASE_B_CURRENT(13032, INT16, 0.1f, Units.AMPERE), - PHASE_C_CURRENT(13033, INT16, 0.1f, Units.AMPERE), - TOTAL_ACTIVE_POWER(13034, INT32_SWAP, 1, Units.WATT), - DAILY_IMPORT_ENERGY(13036, UINT16, 0.1f, Units.KILOWATT_HOUR), - TOTAL_IMPORT_ENERGY(13037, UINT32_SWAP, 0.1f, Units.KILOWATT_HOUR), - BATTERY_CAPACITY(13039, UINT16, 0.1f, Units.KILOWATT_HOUR), - DAILY_CHARGE_ENERGY(13040, UINT16, 0.1f, Units.KILOWATT_HOUR), - TOTAL_CHARGE_ENERGY(13041, UINT32_SWAP, 0.1f, Units.KILOWATT_HOUR), - DRM_STATE(13043, UINT16, 1, Units.ONE), - - DAILY_EXPORT_ENERGY(13045, UINT16, 0.1f, Units.KILOWATT_HOUR), - TOTAL_EXPORT_ENERGY(13046, UINT32_SWAP, 0.1f, Units.KILOWATT_HOUR), - - INVERTER_ALARM(13050, UINT32_SWAP, 1, Units.ONE), - GRID_SIDE_FAULT(13052, UINT32_SWAP, 1, Units.ONE), - SYSTEM_FAULT_1(13054, UINT32_SWAP, 1, Units.ONE), - SYSTEM_FAULT_2(13056, UINT32_SWAP, 1, Units.ONE), - DC_SIDE_FAULT(13058, UINT32_SWAP, 1, Units.ONE), - PERMANENT_FAULT(13060, UINT32_SWAP, 1, Units.ONE), - BDC_SIDE_FAULT(13062, UINT32_SWAP, 1, Units.ONE), - BDC_SIDE_PERMANENT_FAULT(13064, UINT32_SWAP, 1, Units.ONE), - BATTERY_FAULT(13066, UINT32_SWAP, 1, Units.ONE), - BATTERY_ALARM(13068, UINT32_SWAP, 1, Units.ONE), - BMS_ALARM_1(13070, UINT32_SWAP, 1, Units.ONE), - BMS_PROTECTION(13072, UINT32_SWAP, 1, Units.ONE), - BMS_FAULT_1(13074, UINT32_SWAP, 1, Units.ONE), - BMS_FAULT_2(13076, UINT32_SWAP, 1, Units.ONE), - BMS_ALARM_2(13078, UINT32_SWAP, 1, Units.ONE); + DAILY_PV_GENERATION(13002, UINT16, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "overview"), + TOTAL_PV_GENERATION(13003, UINT32_SWAP, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "overview"), + DAILY_EXPORT_POWER_FROM_PV(13005, UINT16, 100, QUANTITY_FACTORY(Units.WATT), "grid_information"), + TOTAL_EXPORT_ENERGY_FROM_PV(13006, UINT32_SWAP, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "grid_information"), + LOAD_POWER(13008, INT32_SWAP, 1, QUANTITY_FACTORY(Units.WATT), "load_information"), + EXPORT_POWER(13010, INT32_SWAP, 1, QUANTITY_FACTORY(Units.WATT), "grid_information"), + DAILY_BATTERY_CHARGE(13012, UINT16, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "battery_information"), + TOTAL_BATTERY_CHARGE(13013, UINT32_SWAP, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "battery_information"), + /* + * Not working + * CO2_REDUCTION(13015, UINT32_SWAP, 0.1f, tech.units.indriya.unit.Units.KILOGRAM), + */ + DAILY_DIRECT_ENERGY_CONSUMPTION(13017, UINT16, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "load_information"), + TOTAL_DIRECT_ENERGY_CONSUMPTION(13018, UINT32_SWAP, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), + "load_information"), + BATTERY_VOLTAGE(13020, UINT16, 0.1f, QUANTITY_FACTORY(Units.VOLT), "battery_information"), + BATTERY_CURRENT(13021, UINT16, 0.1f, QUANTITY_FACTORY(Units.AMPERE), "battery_information"), + BATTERY_POWER(13022, UINT16, 1, QUANTITY_FACTORY(Units.WATT), "battery_information"), + BATTERY_LEVEL(13023, UINT16, 0.1f, PercentType::new, "battery_information"), + BATTERY_HEALTHY(13024, UINT16, 0.1f, PercentType::new, "battery_information"), + BATTERY_TEMPERATUR(13025, INT16, 0.1f, QUANTITY_FACTORY(Units.KELVIN), ConversionConstants.CELSIUS_TO_KELVIN, + "battery_information"), + DAILY_BATTERY_DISCHARGE_ENERGY(13026, UINT16, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "battery_information"), + TOTAL_BATTERY_DISCHARGE_ENERGY(13027, UINT32_SWAP, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), + "battery_information"), + SELF_CONSUMPTION_TODAY(13029, UINT16, 0.1f, PercentType::new, "load_information"), + // Not working + // GRID_STATE(13030, UINT16, 1, quantityTypeFactory(Units.ONE, "grid_information"), + PHASE_A_CURRENT(13031, INT16, 0.1f, QUANTITY_FACTORY(Units.AMPERE), "overview"), + PHASE_B_CURRENT(13032, INT16, 0.1f, QUANTITY_FACTORY(Units.AMPERE), "overview"), + PHASE_C_CURRENT(13033, INT16, 0.1f, QUANTITY_FACTORY(Units.AMPERE), "overview"), + TOTAL_ACTIVE_POWER(13034, INT32_SWAP, 1, QUANTITY_FACTORY(Units.WATT), "overview"), + DAILY_IMPORT_ENERGY(13036, UINT16, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "grid_information"), + TOTAL_IMPORT_ENERGY(13037, UINT32_SWAP, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "grid_information"), + BATTERY_CAPACITY(13039, UINT16, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "battery_information"), + DAILY_CHARGE_ENERGY(13040, UINT16, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "battery_information"), + TOTAL_CHARGE_ENERGY(13041, UINT32_SWAP, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "battery_information"), + // DRM_STATE(13043, UINT16, 1, quantityTypeFactory(Units.ONE, "channelGroup"), + + DAILY_EXPORT_ENERGY(13045, UINT16, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "grid_information"), + TOTAL_EXPORT_ENERGY(13046, UINT32_SWAP, 0.1f, QUANTITY_FACTORY(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 Unit unit; private final Function conversion; + private final Function stateFactory; + private final String channelGroup; - SungrowInverterRegisters(int registerNumber, ValueType type, float multiplier, Unit unit, - Function conversion) { + SungrowInverterRegisters(int registerNumber, ValueType type, float multiplier, + Function stateFactory, Function conversion, + String channelGroup) { this.multiplier = new BigDecimal(multiplier); this.registerNumber = registerNumber; this.type = type; - this.unit = unit; this.conversion = conversion; + this.stateFactory = stateFactory; + this.channelGroup = channelGroup; } - private SungrowInverterRegisters(int registerNumber, ValueType type, float multiplier, Unit unit) { + private SungrowInverterRegisters(int registerNumber, ValueType type, float multiplier, + Function stateFactory, String channelGroup) { this.multiplier = new BigDecimal(multiplier); this.registerNumber = registerNumber; this.type = type; - this.unit = unit; this.conversion = Function.identity(); + this.stateFactory = stateFactory; + this.channelGroup = channelGroup; } - public Unit getUnit() { - return unit; - } - - public BigDecimal getMultiplier() { - return multiplier; + /** + * Creates a Function that creates {@link QuantityType} states with the given {@link Unit}. + */ + private static Function QUANTITY_FACTORY(Unit unit) { + return (BigDecimal value) -> new QuantityType<>(value, unit); } public int getRegisterNumber() { @@ -164,9 +191,18 @@ public int getRegisterCount() { } /** - * Returns the value conversion. + * Returns the channel group. + */ + public String getChannelGroup() { + return channelGroup; + } + + /** + * Creates the {@link State} for the given register value. */ - public Function getConversion() { - return conversion; + public State createState(DecimalType registerValue) { + final BigDecimal scaledValue = registerValue.toBigDecimal().multiply(this.multiplier); + final BigDecimal convertedValue = conversion.apply(scaledValue); + return this.stateFactory.apply(convertedValue); } } diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersBackup.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersBackup.java deleted file mode 100644 index 17fa732f61693..0000000000000 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersBackup.java +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright (c) 2010-2023 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.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; -import static org.openhab.core.io.transport.modbus.ModbusConstants.ValueType.UINT16; -import static org.openhab.core.io.transport.modbus.ModbusConstants.ValueType.UINT32; - -import java.math.BigDecimal; - -import javax.measure.Unit; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.core.io.transport.modbus.ModbusConstants.ValueType; -import org.openhab.core.library.unit.Units; - -/** - * The {@link SungrowInverterRegistersBackup} is responsible for defining Modbus registers and their units. - * - * @author Sönke Küper - Initial contribution - */ -@NonNullByDefault -public enum SungrowInverterRegistersBackup { - - // the following register numbers are 1-based. They need to be converted before sending them on the wire. - NOMINAL_ACTIVE_POWER(100, 5001, UINT16, Units.WATT), - DAILY_POWER_YIELDS(0.1f, 5003, UINT16, Units.KILOWATT_HOUR), - TOTAL_POWER_YIELDS(1, 5004, UINT32, Units.KILOWATT_HOUR), - TOTAL_RUNNING_TIME(0.1f, 5006, UINT32, Units.HOUR), - INTERNAL_TEMPERATURE(0.1f, 5008, INT16, Units.KELVIN), // TODO: Umrechnung C in K + 273,15 - TOTAL_APPARENT_POWER(1, 5009, UINT32, Units.VOLT_AMPERE), - MPPT1_VOLTAGE(0.1f, 5011, UINT16, Units.VOLT), - MPPT1_CURRENT(0.1f, 5012, UINT16, Units.AMPERE), - MPPT2_VOLTAGE(0.1f, 5013, UINT16, Units.VOLT), - MPPT2_CURRENT(0.1f, 5014, UINT16, Units.AMPERE), - MPPT3_VOLTAGE(0.1f, 5015, UINT16, Units.VOLT), - MPPT3_CURRENT(0.1f, 5016, UINT16, Units.AMPERE), - TOTAL_DC_POWER(1, 5017, UINT32, Units.WATT), - PHASE_A_VOLTAGE(0.1f, 5019, UINT16, Units.VOLT), - PHASE_B_VOLTAGE(0.1f, 5020, UINT16, Units.VOLT), - PHASE_C_VOLTAGE(0.1f, 5021, UINT16, Units.VOLT), - PHASE_A_CURRENT(0.1f, 5022, UINT16, Units.AMPERE), - PHASE_B_CURRENT(0.1f, 5023, UINT16, Units.AMPERE), - PHASE_C_CURRENT(0.1f, 5024, UINT16, Units.AMPERE), - TOTAL_ACTIVE_POWER(1, 5031, UINT32, Units.WATT), - TOTAL_REACTIVE_POWER(1, 5033, INT32, Units.VAR), - POWER_FACTOR(0.001f, 5035, INT16, Units.ONE), - GRID_FREQUENCY(0.1f, 5036, UINT16, Units.HERTZ), - WORK_STATE_1(1, 5038, UINT16, Units.ONE), - NOMINAL_REACTIVE_POWER(0.1f, 5049, UINT16, Units.KILOVAR), - ARRAY_INSULATION_RESISTANCE(1000, 5071, UINT16, Units.OHM), - ACTIVE_POWER_REGULATION_SETPOINT(1, 5077, UINT32, Units.WATT), - REACTIVE_POWER_REGULATION_SETPOINT(1, 5079, INT32, Units.VAR), - WORK_STATE_2(1, 5081, UINT32, Units.ONE), - METER_POWER(1, 5083, INT32, Units.WATT), - METER_PHASE_A_POWER(1, 5085, INT32, Units.WATT), - METER_PHASE_B_POWER(1, 5087, INT32, Units.WATT), - METER_PHASE_C_POWER(1, 5089, INT32, Units.WATT), - LOAD_POWER(1, 5091, INT32, Units.WATT), - DAILY_EXPORT_ENERGY(0.1f, 5093, UINT32, Units.KILOWATT_HOUR), - TOTAL_EXPORT_ENERGY(0.1f, 5095, UINT32, Units.KILOWATT_HOUR), - DAILY_IMPORT_ENERGY(0.1f, 5097, UINT32, Units.KILOWATT_HOUR), - TOTAL_IMPORT_ENERGY(0.1f, 5099, UINT32, Units.KILOWATT_HOUR), - DAILY_DIRECT_ENERGY_CONSUMPTION(0.1f, 5101, UINT32, Units.KILOWATT_HOUR), - TOTAL_DIRECT_ENERGY_CONSUMPTION(0.1f, 5103, UINT32, Units.KILOWATT_HOUR), - DAILY_RUNNING_TIME(1, 5113, UINT16, Units.MINUTE), - MONTHLY_POWER_YIELDS(0.1f, 5128, UINT32, Units.KILOWATT_HOUR), - TOTAL_POWER_YIELDS_FINE(0.1f, 5144, UINT32, Units.KILOWATT_HOUR), - NEGATIVE_VOLTAGE_TO_GROUND(0.1f, 5146, INT16, Units.VOLT), - BUS_VOLTAGE(0.1f, 5147, UINT16, Units.VOLT), - GRID_FREQUENCY_FINE(0.01f, 5148, UINT16, Units.HERTZ), - PID_WORK_STATE(1, 5150, UINT16, Units.ONE), - PID_ALARM_CODE(1, 5151, UINT16, Units.ONE); - - private final BigDecimal multiplier; - private final int registerNumber; - private final ValueType type; - private final Unit unit; - - private SungrowInverterRegistersBackup(float multiplier, int registerNumber, ValueType type, Unit unit) { - this.multiplier = new BigDecimal(multiplier); - this.registerNumber = registerNumber; - this.type = type; - this.unit = unit; - } - - public Unit getUnit() { - return unit; - } - - public BigDecimal getMultiplier() { - return multiplier; - } - - public int getRegisterNumber() { - return registerNumber; - } - - public ValueType getType() { - return type; - } - - /** - * Returns the count of registers read to return the value of this register. - */ - public int getRegisterCount() { - return this.type.getBits() / 16; - } -} diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SystemState.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SystemState.java deleted file mode 100644 index 7bd7084691b1d..0000000000000 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SystemState.java +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (c) 2010-2023 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.modbus.sungrow.internal; - -import java.util.Arrays; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; - -/** - * Definition of Sungrow system state codes. - * - * @author Sönke Küper - Initial contribution - */ -@NonNullByDefault -public enum SystemState { - - STOP("Stop", 0x0002), - STANDBY("Standby", 0x0008), - INIT_STANDBY("Initial standby", 0x0010), - STARTUP("Startup", 0x0020), - RUNNING("Running", 0x0040), - FAULT("Fault", 0x0100), - MAINTAIN_MODE("Running in maintain mode", 0x0400), - FORCED_MODE("Running in forced mode", 0x0800), - OFF_GRID("Running in off-grid mode", 0x1000), - RESTARTING("Restarting", 0x2501), - EXTERNAL_EMS("Running in External EMS mode", 0x4000); - - private static final Map CODE_MAPPING = initMapping(); - - private static Map initMapping() { - return Arrays.stream(SystemState.values()) - .collect(Collectors.toMap(SystemState::getStateCode, Function.identity())); - } - - /** - * Returns the {@link SystemState} for the given state code. - */ - @Nullable - public static SystemState getByStateCode(int stateCode) { - return CODE_MAPPING.get(stateCode); - } - - private final int stateCode; - private final String description; - - SystemState(String description, int stateCode) { - this.stateCode = stateCode; - this.description = description; - } - - public int getStateCode() { - return stateCode; - } - - public String getDescription() { - return description; - } -} diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/i18n/sungrow.properties b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/i18n/sungrow.properties index 15bba7b7c6af2..510aeeb41dd76 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/i18n/sungrow.properties +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/i18n/sungrow.properties @@ -8,69 +8,57 @@ thing-type.modbus.sungrowInverter.description = Sungrow inverter connected via M 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.battery_alarm.label = BATTERY_ALARM -channel-type.modbus.battery_capacity.label = Battery Capacity -channel-type.modbus.battery_current.label = Battery Current -channel-type.modbus.battery_fault.label = BATTERY_FAULT -channel-type.modbus.battery_healthy.label = Battery Health (SOH) -channel-type.modbus.battery_level.label = Battery Level (SOC) -channel-type.modbus.battery_power.label = BATTERY_POWER -channel-type.modbus.battery_temperatur.label = Battery Temperature -channel-type.modbus.battery_voltage.label = Battery Voltage -channel-type.modbus.bdc_side_fault.label = BDC_SIDE_FAULT -channel-type.modbus.bdc_side_permanent_fault.label = BDC_SIDE_PERMANENT_FAULT -channel-type.modbus.bms_alarm_1.label = BMS_ALARM_1 -channel-type.modbus.bms_alarm_2.label = BMS_ALARM_2 -channel-type.modbus.bms_fault_1.label = BMS_FAULT_1 -channel-type.modbus.bms_fault_2.label = BMS_FAULT_2 -channel-type.modbus.bms_protection.label = BMS_PROTECTION -channel-type.modbus.co2_reduction.label = Co2 Reduction -channel-type.modbus.daily_battery_charge.label = Daily Battery Charging Energy from PV -channel-type.modbus.daily_battery_discharge_energy.label = Daily Battery Discharging Energy -channel-type.modbus.daily_charge_energy.label = Daily Battery Charging Energy -channel-type.modbus.daily_direct_energy_consumption.label = Daily Load Energy Consumption from PV -channel-type.modbus.daily_export_energy.label = Daily Feed-in Energy -channel-type.modbus.daily_export_power_from_pv.label = Daily Feed-in Energy (PV) -channel-type.modbus.daily_import_energy.label = Daily Purchased Energy -channel-type.modbus.daily_output_energy.label = DAILY_OUTPUT_ENERGY -channel-type.modbus.daily_pv_generation.label = Daily PV Yield -channel-type.modbus.dc_side_fault.label = DC_SIDE_FAULT -channel-type.modbus.drm_state.label = DRM_STATE -channel-type.modbus.export_power.label = Total Export Active Power -channel-type.modbus.grid_frequency.label = Grid Frequency -channel-type.modbus.grid_side_fault.label = GRID_SIDE_FAULT -channel-type.modbus.grid_state.label = GRID_STATE -channel-type.modbus.internal_temperature.label = Internal Temperature -channel-type.modbus.inverter_alarm.label = INVERTER_ALARM -channel-type.modbus.load_power.label = Load Power -channel-type.modbus.mppt1_current.label = MPPT1 Current -channel-type.modbus.mppt1_voltage.label = MPPT1 Voltage -channel-type.modbus.mppt2_current.label = MPPT2 Current -channel-type.modbus.mppt2_voltage.label = MPPT2 Voltage -channel-type.modbus.permanent_fault.label = PERMANENT_FAULT -channel-type.modbus.phase_a_current.label = Phase A Current -channel-type.modbus.phase_a_voltage.label = Phase A Voltage -channel-type.modbus.phase_b_current.label = Phase B Current -channel-type.modbus.phase_b_voltage.label = Phase B Voltage -channel-type.modbus.phase_c_current.label = Phase C Current -channel-type.modbus.phase_c_voltage.label = Phase C Voltage -channel-type.modbus.power_factor.label = Total Power Factor -channel-type.modbus.reactive_power.label = Total Reactive Power -channel-type.modbus.running_state.label = Running Status -channel-type.modbus.self_consumption_today.label = Daily Self-consumption Rate -channel-type.modbus.system_fault_1.label = SYSTEM_FAULT_1 -channel-type.modbus.system_fault_2.label = SYSTEM_FAULT_2 -channel-type.modbus.system_state.label = Current Status -channel-type.modbus.total_active_power.label = Total Active Power -channel-type.modbus.total_battery_charge.label = Total Battery Charging Energy -channel-type.modbus.total_battery_discharge_energy.label = Total Battery Discharging Energy -channel-type.modbus.total_charge_energy.label = Total Battery Charging Energy from PV -channel-type.modbus.total_dc_power.label = Total DC Power -channel-type.modbus.total_direct_energy_consumption.label = Total Load Energy Consumption from PV -channel-type.modbus.total_export_energy.label = Total Feed-in Energy -channel-type.modbus.total_export_energy_from_pv.label = Total Feed-in Energy (PV) -channel-type.modbus.total_import_energy.label = Total Purchased Energy -channel-type.modbus.total_output_energy.label = TOTAL_OUTPUT_ENERGY -channel-type.modbus.total_pv_generation.label = Total PV Yield +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 diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/i18n/sungrow_de.properties b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/i18n/sungrow_de.properties index 8170b6483f50d..5b2df586ec129 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/i18n/sungrow_de.properties +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/i18n/sungrow_de.properties @@ -8,69 +8,57 @@ thing-type.modbus.sungrowInverter.description = Sungrow inverter connected via M 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 = Überblick +channel-group-type.modbus.sg-mppt_information.label = MPPT +channel-group-type.modbus.sg-battery_information.label = Batterie +channel-group-type.modbus.sg-load_information.label = Last +channel-group-type.modbus.sg-grid_information.label = Stromnetz + # channel types -channel-type.modbus.battery_alarm.label = BATTERY_ALARM -channel-type.modbus.battery_capacity.label = Batteriekapazität -channel-type.modbus.battery_current.label = Batteriestrom -channel-type.modbus.battery_fault.label = BATTERY_FAULT -channel-type.modbus.battery_healthy.label = Gesundheit der Batterie (SOH) -channel-type.modbus.battery_level.label = Batteriestand -channel-type.modbus.battery_power.label = BATTERY_POWER -channel-type.modbus.battery_temperatur.label = Batterietemperatur -channel-type.modbus.battery_voltage.label = Batteriespannung -channel-type.modbus.bdc_side_fault.label = BDC_SIDE_FAULT -channel-type.modbus.bdc_side_permanent_fault.label = BDC_SIDE_PERMANENT_FAULT -channel-type.modbus.bms_alarm_1.label = BMS_ALARM_1 -channel-type.modbus.bms_alarm_2.label = BMS_ALARM_2 -channel-type.modbus.bms_fault_1.label = BMS_FAULT_1 -channel-type.modbus.bms_fault_2.label = BMS_FAULT_2 -channel-type.modbus.bms_protection.label = BMS_PROTECTION -channel-type.modbus.co2_reduction.label = Co2 Einsparung -channel-type.modbus.daily_battery_charge.label = Eingespeicherte PV-Energie (Tagesbasis) -channel-type.modbus.daily_battery_discharge_energy.label = Aus Batterie entnommen (Tagesbasis) -channel-type.modbus.daily_charge_energy.label = In Batterie gespeichert (Tagesbasis) -channel-type.modbus.daily_direct_energy_consumption.label = Tages-PV-Eigenverbrauch -channel-type.modbus.daily_export_energy.label = Tägliche Netzeinspeisung -channel-type.modbus.daily_export_power_from_pv.label = Tägliches PV-Einspeisenetzvolumen -channel-type.modbus.daily_import_energy.label = Tägliche Energie abgenommen vom Netz -channel-type.modbus.daily_output_energy.label = DAILY_OUTPUT_ENERGY -channel-type.modbus.daily_pv_generation.label = Tägliche PV-Stromerzeugung -channel-type.modbus.dc_side_fault.label = DC_SIDE_FAULT -channel-type.modbus.drm_state.label = DRM_STATE -channel-type.modbus.export_power.label = Wirkleistung Einspeisung -channel-type.modbus.grid_frequency.label = Netzfrequenz -channel-type.modbus.grid_side_fault.label = GRID_SIDE_FAULT -channel-type.modbus.grid_state.label = GRID_STATE -channel-type.modbus.internal_temperature.label = Temperatur -channel-type.modbus.inverter_alarm.label = INVERTER_ALARM -channel-type.modbus.load_power.label = Lastleistung -channel-type.modbus.mppt1_current.label = MPPT1 Strom -channel-type.modbus.mppt1_voltage.label = MPPT1 Spannung -channel-type.modbus.mppt2_current.label = MPPT2 Strom -channel-type.modbus.mppt2_voltage.label = MPPT2 Spannung -channel-type.modbus.permanent_fault.label = PERMANENT_FAULT -channel-type.modbus.phase_a_current.label = Strom Phase A -channel-type.modbus.phase_a_voltage.label = Spannung Phase A -channel-type.modbus.phase_b_current.label = Strom Phase B -channel-type.modbus.phase_b_voltage.label = Spannung Phase B -channel-type.modbus.phase_c_current.label = Strom Phase C -channel-type.modbus.phase_c_voltage.label = Spannung Phase C -channel-type.modbus.power_factor.label = Leistungsfaktor -channel-type.modbus.reactive_power.label = Blindleistung -channel-type.modbus.running_state.label = Betriebsstatus -channel-type.modbus.self_consumption_today.label = Tägliche Eigenverbrauchsrate -channel-type.modbus.system_fault_1.label = SYSTEM_FAULT_1 -channel-type.modbus.system_fault_2.label = SYSTEM_FAULT_2 -channel-type.modbus.system_state.label = Aktueller Zustand -channel-type.modbus.total_active_power.label = Wirkleistung Gesamt -channel-type.modbus.total_battery_charge.label = Eingespeicherte PV-Energie (Gesamt) -channel-type.modbus.total_battery_discharge_energy.label = Aus Batterie entnommen (Gesamt) -channel-type.modbus.total_charge_energy.label = In Batterie gespeichert (Gesamt) -channel-type.modbus.total_dc_power.label = PV-Leistung -channel-type.modbus.total_direct_energy_consumption.label = Gesamter PV-Eigenverbrauch -channel-type.modbus.total_export_energy.label = Netzeinspeisung gesamt -channel-type.modbus.total_export_energy_from_pv.label = Gesamt-Verlauf PV Einspeisemenge -channel-type.modbus.total_import_energy.label = Netzbezug insgesamt -channel-type.modbus.total_output_energy.label = TOTAL_OUTPUT_ENERGY -channel-type.modbus.total_pv_generation.label = Gesamte PV-Stromerzeugung +channel-type.modbus.sg-battery_capacity.label = Batteriekapazität +channel-type.modbus.sg-battery_current.label = Batteriestrom +channel-type.modbus.sg-battery_healthy.label = Gesundheit der Batterie (SOH) +channel-type.modbus.sg-battery_level.label = Batteriestand +channel-type.modbus.sg-battery_power.label = Batterieleistung +channel-type.modbus.sg-battery_temperature.label = Batterietemperatur +channel-type.modbus.sg-battery_voltage.label = Batteriespannung +channel-type.modbus.sg-co2_reduction.label = Co2 Einsparung +channel-type.modbus.sg-daily_battery_charge.label = Eingespeicherte PV-Energie (Tagesbasis) +channel-type.modbus.sg-daily_battery_discharge_energy.label = Aus Batterie entnommen (Tagesbasis) +channel-type.modbus.sg-daily_charge_energy.label = In Batterie gespeichert (Tagesbasis) +channel-type.modbus.sg-daily_direct_energy_consumption.label = Tages-PV-Eigenverbrauch +channel-type.modbus.sg-daily_export_energy.label = Tägliche Netzeinspeisung +channel-type.modbus.sg-daily_export_power_from_pv.label = Tägliches PV-Einspeisenetzvolumen +channel-type.modbus.sg-daily_import_energy.label = Tägliche Energie abgenommen vom Netz +channel-type.modbus.sg-daily_pv_generation.label = Tägliche PV-Stromerzeugung +channel-type.modbus.sg-export_power.label = Wirkleistung Einspeisung +channel-type.modbus.sg-grid_frequency.label = Netzfrequenz +channel-type.modbus.sg-internal_temperature.label = Temperatur +channel-type.modbus.sg-load_power.label = Lastleistung +channel-type.modbus.sg-mppt1_current.label = MPPT1 Strom +channel-type.modbus.sg-mppt1_voltage.label = MPPT1 Spannung +channel-type.modbus.sg-mppt2_current.label = MPPT2 Strom +channel-type.modbus.sg-mppt2_voltage.label = MPPT2 Spannung +channel-type.modbus.sg-phase_a_current.label = Strom Phase A +channel-type.modbus.sg-phase_a_voltage.label = Spannung Phase A +channel-type.modbus.sg-phase_b_current.label = Strom Phase B +channel-type.modbus.sg-phase_b_voltage.label = Spannung Phase B +channel-type.modbus.sg-phase_c_current.label = Strom Phase C +channel-type.modbus.sg-phase_c_voltage.label = Spannung Phase C +channel-type.modbus.sg-power_factor.label = Leistungsfaktor +channel-type.modbus.sg-reactive_power.label = Blindleistung +channel-type.modbus.sg-running_state.label = Betriebsstatus +channel-type.modbus.sg-self_consumption_today.label = Tägliche Eigenverbrauchsrate +channel-type.modbus.sg-system_state.label = Aktueller Zustand +channel-type.modbus.sg-total_active_power.label = Wirkleistung Gesamt +channel-type.modbus.sg-total_battery_charge.label = Eingespeicherte PV-Energie (Gesamt) +channel-type.modbus.sg-total_battery_discharge_energy.label = Aus Batterie entnommen (Gesamt) +channel-type.modbus.sg-total_charge_energy.label = In Batterie gespeichert (Gesamt) +channel-type.modbus.sg-total_dc_power.label = PV-Leistung +channel-type.modbus.sg-total_direct_energy_consumption.label = Gesamter PV-Eigenverbrauch +channel-type.modbus.sg-total_export_energy.label = Netzeinspeisung gesamt +channel-type.modbus.sg-total_export_energy_from_pv.label = Gesamt-Verlauf PV Einspeisemenge +channel-type.modbus.sg-total_import_energy.label = Netzbezug insgesamt +channel-type.modbus.sg-total_pv_generation.label = Gesamte PV-Stromerzeugung diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml index e388514fd25cd..7d3fd5a1ca9ab 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml @@ -12,394 +12,515 @@ Sungrow inverter connected via Modbus. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Inverter + + + + + + + - - Number:Energy - - - - - Number:Energy - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Number:Temperature + Temperature + + Measurement + Temperature + - + Number:ElectricPotential + Energy + + Measurement + Voltage + - + Number:ElectricCurrent + Energy + + Measurement + Current + - + Number:ElectricPotential + Energy + + Measurement + Voltage + - + Number:ElectricCurrent + Energy + + Measurement + Current + - + Number:Power + Energy + + Measurement + Power + - + Number:ElectricPotential + Energy + + Measurement + Voltage + - + Number:ElectricPotential + Energy + + Measurement + Voltage + - + Number:ElectricPotential + Energy + + Measurement + Voltage + - + Number:Power + Energy + + Measurement + Power + - + Number:Dimensionless + Energy - + Number:Frequency + + Measurement + Frequency + - - Number:Dimensionless - - - - - Number:Energy - - - - + + Number:Energy + Energy + + Measurement + Energy + - + Number:Energy + Energy + + Measurement + Energy + - + Number:Power + Energy + + Measurement + Power + - + Number:Energy + Energy + + Measurement + Energy + - + Number:Power + Energy + + Measurement + Power + - + Number:Power + Energy + + Measurement + Power + - + Number:Energy + Energy + + Measurement + Energy + - + Number:Energy + Energy + + Measurement + Energy + - + + Number:Energy + Energy + + Measurement + Energy + - + Number:Energy + Energy + + Measurement + Energy + - + Number:ElectricPotential + Energy + + Measurement + Voltage + - + Number:ElectricCurrent + Energy + + Measurement + Current + - + Number:Power + Battery + + Measurement + Power + - - Number:Dimensionless + + Number - - - - Number:Dimensionless + Battery + + Measurement + Energy + + + + + Number - + Battery + - + Number:Temperature + Battery + + Measurement + Temperature + - + Number:Energy + Energy + + Measurement + Energy + - + Number:Energy + Energy + + Measurement + Energy + - - Number:Dimensionless + + Number - + - + + Number:Dimensionless - - + + --> + Number:ElectricCurrent + Energy + + Measurement + Current + - + Number:ElectricCurrent + Energy + + Measurement + Current + - + Number:ElectricCurrent + Energy + + Measurement + Current + - + Number:Power + Energy + + Measurement + Power + - + Number:Energy + Energy + + Measurement + Energy + - + Number:Energy + Energy + + Measurement + Energy + - + Number:Energy + Energy + + Measurement + Energy + - + Number:Energy + Energy + + Measurement + Energy + - + Number:Energy + Energy + + Measurement + Energy + - - Number:Dimensionless - - - - + + Number:Energy + Energy + + Measurement + Energy + - + Number:Energy + Energy + + Measurement + Energy + - - Number:Dimensionless - - - - - Number:Dimensionless - - - - - Number:Dimensionless - - - - - Number:Dimensionless - - - - - Number:Dimensionless - - - - - Number:Dimensionless - - - - - Number:Dimensionless - - - - - Number:Dimensionless - - - - - Number:Dimensionless - - - - - Number:Dimensionless - - - - - Number:Dimensionless - - - - - Number:Dimensionless - - - - - Number:Dimensionless - - - - - Number:Dimensionless - - - - - Number:Dimensionless - - - - diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/test/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersTest.java b/bundles/org.openhab.binding.modbus.sungrow/src/test/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersTest.java deleted file mode 100644 index 09da71ed491b7..0000000000000 --- a/bundles/org.openhab.binding.modbus.sungrow/src/test/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersTest.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) 2010-2023 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.modbus.sungrow.internal; - -import org.junit.jupiter.api.Test; - -/** - * Tests - * - * @author Sönke Küper - Initial contribution - */ -class SungrowInverterRegistersTest { - - @Test - public void test() { - for (SungrowInverterRegisters register : SungrowInverterRegisters.values()) { - System.out.printf("\t\n", register.name().toLowerCase(), - register.name().toLowerCase()); - - } - - for (SungrowInverterRegisters register : SungrowInverterRegisters.values()) { - System.out.printf( - "\t\n" + "\t\t%s\n" + "\t\t\n" - + "\t\t\n" + "\t\n", - register.name().toLowerCase(), register.getUnit(), register.name()); - - } - } -} From e56343586c1cb9aa1ab4301e4188648e249f73e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20K=C3=BCper?= Date: Mon, 18 Dec 2023 21:15:08 +0100 Subject: [PATCH 04/30] 0000: Fixed checkstyle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- .../internal/SungrowInverterRegisters.java | 81 +++++++++---------- 1 file changed, 40 insertions(+), 41 deletions(-) diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java index ced8db703de20..7da49b9b1854b 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java @@ -43,20 +43,20 @@ public enum SungrowInverterRegisters { // DAILY_OUTPUT_ENERGY(5003, UINT16, 0.1f, quantityTypeFactory(Units.KILOWATT_HOUR), // TOTAL_OUTPUT_ENERGY(5004, UINT32_SWAP, 0.1f, quantityTypeFactory(Units.KILOWATT_HOUR), - INTERNAL_TEMPERATURE(5008, INT16, 0.1f, QUANTITY_FACTORY(Units.KELVIN), ConversionConstants.CELSIUS_TO_KELVIN, + INTERNAL_TEMPERATURE(5008, INT16, 0.1f, quantityFactory(Units.KELVIN), ConversionConstants.CELSIUS_TO_KELVIN, "overview"), - MPPT1_VOLTAGE(5011, UINT16, 0.1f, QUANTITY_FACTORY(Units.VOLT), "mppt_information"), - MPPT1_CURRENT(5012, UINT16, 0.1f, QUANTITY_FACTORY(Units.AMPERE), "mppt_information"), - MPPT2_VOLTAGE(5013, UINT16, 0.1f, QUANTITY_FACTORY(Units.VOLT), "mppt_information"), - MPPT2_CURRENT(5014, UINT16, 0.1f, QUANTITY_FACTORY(Units.AMPERE), "mppt_information"), - TOTAL_DC_POWER(5017, UINT32_SWAP, 1, QUANTITY_FACTORY(Units.WATT), "overview"), - PHASE_A_VOLTAGE(5019, UINT16, 0.1f, QUANTITY_FACTORY(Units.VOLT), "overview"), - PHASE_B_VOLTAGE(5020, UINT16, 0.1f, QUANTITY_FACTORY(Units.VOLT), "overview"), - PHASE_C_VOLTAGE(5021, UINT16, 0.1f, QUANTITY_FACTORY(Units.VOLT), "overview"), - - REACTIVE_POWER(5033, INT32_SWAP, 1, QUANTITY_FACTORY(Units.VAR), "overview"), + MPPT1_VOLTAGE(5011, UINT16, 0.1f, quantityFactory(Units.VOLT), "mppt_information"), + MPPT1_CURRENT(5012, UINT16, 0.1f, quantityFactory(Units.AMPERE), "mppt_information"), + MPPT2_VOLTAGE(5013, UINT16, 0.1f, quantityFactory(Units.VOLT), "mppt_information"), + MPPT2_CURRENT(5014, UINT16, 0.1f, quantityFactory(Units.AMPERE), "mppt_information"), + TOTAL_DC_POWER(5017, UINT32_SWAP, 1, quantityFactory(Units.WATT), "overview"), + PHASE_A_VOLTAGE(5019, UINT16, 0.1f, quantityFactory(Units.VOLT), "overview"), + PHASE_B_VOLTAGE(5020, UINT16, 0.1f, quantityFactory(Units.VOLT), "overview"), + PHASE_C_VOLTAGE(5021, UINT16, 0.1f, quantityFactory(Units.VOLT), "overview"), + + REACTIVE_POWER(5033, INT32_SWAP, 1, quantityFactory(Units.VAR), "overview"), POWER_FACTOR(5035, INT16, 0.001f, DecimalType::new, "overview"), - GRID_FREQUENCY(5036, UINT16, 0.01f, QUANTITY_FACTORY(Units.HERTZ), "overview"), + GRID_FREQUENCY(5036, UINT16, 0.01f, quantityFactory(Units.HERTZ), "overview"), /* * Not working @@ -76,47 +76,46 @@ public enum SungrowInverterRegisters { * RUNNING_STATE(13001, UINT16, 1, quantityTypeFactory(Units.ONE)), */ - DAILY_PV_GENERATION(13002, UINT16, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "overview"), - TOTAL_PV_GENERATION(13003, UINT32_SWAP, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "overview"), - DAILY_EXPORT_POWER_FROM_PV(13005, UINT16, 100, QUANTITY_FACTORY(Units.WATT), "grid_information"), - TOTAL_EXPORT_ENERGY_FROM_PV(13006, UINT32_SWAP, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "grid_information"), - LOAD_POWER(13008, INT32_SWAP, 1, QUANTITY_FACTORY(Units.WATT), "load_information"), - EXPORT_POWER(13010, INT32_SWAP, 1, QUANTITY_FACTORY(Units.WATT), "grid_information"), - DAILY_BATTERY_CHARGE(13012, UINT16, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "battery_information"), - TOTAL_BATTERY_CHARGE(13013, UINT32_SWAP, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "battery_information"), + DAILY_PV_GENERATION(13002, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "overview"), + TOTAL_PV_GENERATION(13003, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "overview"), + DAILY_EXPORT_POWER_FROM_PV(13005, UINT16, 100, quantityFactory(Units.WATT), "grid_information"), + TOTAL_EXPORT_ENERGY_FROM_PV(13006, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "grid_information"), + LOAD_POWER(13008, INT32_SWAP, 1, quantityFactory(Units.WATT), "load_information"), + EXPORT_POWER(13010, INT32_SWAP, 1, quantityFactory(Units.WATT), "grid_information"), + DAILY_BATTERY_CHARGE(13012, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery_information"), + TOTAL_BATTERY_CHARGE(13013, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery_information"), /* * Not working * CO2_REDUCTION(13015, UINT32_SWAP, 0.1f, tech.units.indriya.unit.Units.KILOGRAM), */ - DAILY_DIRECT_ENERGY_CONSUMPTION(13017, UINT16, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "load_information"), - TOTAL_DIRECT_ENERGY_CONSUMPTION(13018, UINT32_SWAP, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), - "load_information"), - BATTERY_VOLTAGE(13020, UINT16, 0.1f, QUANTITY_FACTORY(Units.VOLT), "battery_information"), - BATTERY_CURRENT(13021, UINT16, 0.1f, QUANTITY_FACTORY(Units.AMPERE), "battery_information"), - BATTERY_POWER(13022, UINT16, 1, QUANTITY_FACTORY(Units.WATT), "battery_information"), + DAILY_DIRECT_ENERGY_CONSUMPTION(13017, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "load_information"), + TOTAL_DIRECT_ENERGY_CONSUMPTION(13018, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "load_information"), + BATTERY_VOLTAGE(13020, UINT16, 0.1f, quantityFactory(Units.VOLT), "battery_information"), + BATTERY_CURRENT(13021, UINT16, 0.1f, quantityFactory(Units.AMPERE), "battery_information"), + BATTERY_POWER(13022, UINT16, 1, quantityFactory(Units.WATT), "battery_information"), BATTERY_LEVEL(13023, UINT16, 0.1f, PercentType::new, "battery_information"), BATTERY_HEALTHY(13024, UINT16, 0.1f, PercentType::new, "battery_information"), - BATTERY_TEMPERATUR(13025, INT16, 0.1f, QUANTITY_FACTORY(Units.KELVIN), ConversionConstants.CELSIUS_TO_KELVIN, + BATTERY_TEMPERATUR(13025, INT16, 0.1f, quantityFactory(Units.KELVIN), ConversionConstants.CELSIUS_TO_KELVIN, "battery_information"), - DAILY_BATTERY_DISCHARGE_ENERGY(13026, UINT16, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "battery_information"), - TOTAL_BATTERY_DISCHARGE_ENERGY(13027, UINT32_SWAP, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), + DAILY_BATTERY_DISCHARGE_ENERGY(13026, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery_information"), + TOTAL_BATTERY_DISCHARGE_ENERGY(13027, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery_information"), SELF_CONSUMPTION_TODAY(13029, UINT16, 0.1f, PercentType::new, "load_information"), // Not working // GRID_STATE(13030, UINT16, 1, quantityTypeFactory(Units.ONE, "grid_information"), - PHASE_A_CURRENT(13031, INT16, 0.1f, QUANTITY_FACTORY(Units.AMPERE), "overview"), - PHASE_B_CURRENT(13032, INT16, 0.1f, QUANTITY_FACTORY(Units.AMPERE), "overview"), - PHASE_C_CURRENT(13033, INT16, 0.1f, QUANTITY_FACTORY(Units.AMPERE), "overview"), - TOTAL_ACTIVE_POWER(13034, INT32_SWAP, 1, QUANTITY_FACTORY(Units.WATT), "overview"), - DAILY_IMPORT_ENERGY(13036, UINT16, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "grid_information"), - TOTAL_IMPORT_ENERGY(13037, UINT32_SWAP, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "grid_information"), - BATTERY_CAPACITY(13039, UINT16, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "battery_information"), - DAILY_CHARGE_ENERGY(13040, UINT16, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "battery_information"), - TOTAL_CHARGE_ENERGY(13041, UINT32_SWAP, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "battery_information"), + PHASE_A_CURRENT(13031, INT16, 0.1f, quantityFactory(Units.AMPERE), "overview"), + PHASE_B_CURRENT(13032, INT16, 0.1f, quantityFactory(Units.AMPERE), "overview"), + PHASE_C_CURRENT(13033, INT16, 0.1f, quantityFactory(Units.AMPERE), "overview"), + TOTAL_ACTIVE_POWER(13034, INT32_SWAP, 1, quantityFactory(Units.WATT), "overview"), + DAILY_IMPORT_ENERGY(13036, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "grid_information"), + TOTAL_IMPORT_ENERGY(13037, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "grid_information"), + BATTERY_CAPACITY(13039, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery_information"), + DAILY_CHARGE_ENERGY(13040, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery_information"), + TOTAL_CHARGE_ENERGY(13041, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery_information"), // DRM_STATE(13043, UINT16, 1, quantityTypeFactory(Units.ONE, "channelGroup"), - DAILY_EXPORT_ENERGY(13045, UINT16, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "grid_information"), - TOTAL_EXPORT_ENERGY(13046, UINT32_SWAP, 0.1f, QUANTITY_FACTORY(Units.KILOWATT_HOUR), "grid_information"); + DAILY_EXPORT_ENERGY(13045, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "grid_information"), + TOTAL_EXPORT_ENERGY(13046, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "grid_information"); /* * Status Registers -not known if working so not implemented yet. @@ -171,7 +170,7 @@ private SungrowInverterRegisters(int registerNumber, ValueType type, float multi /** * Creates a Function that creates {@link QuantityType} states with the given {@link Unit}. */ - private static Function QUANTITY_FACTORY(Unit unit) { + private static Function quantityFactory(Unit unit) { return (BigDecimal value) -> new QuantityType<>(value, unit); } From 63c25de6b83c93b8b6129307de2c2a77a3b4d777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20K=C3=BCper?= Date: Mon, 18 Dec 2023 21:15:08 +0100 Subject: [PATCH 05/30] 0000: Fixed Percentage-Values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- .../src/main/resources/OH-INF/thing/thing-types.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml index 7d3fd5a1ca9ab..8953594479f42 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml @@ -355,7 +355,7 @@ - Number + Number:Dimensionless Battery @@ -365,7 +365,7 @@ - Number + Number:Dimensionless Battery @@ -401,7 +401,7 @@ - Number + Number:Dimensionless From 075af5a316aef45154033990ee968b1cba35c52a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20K=C3=BCper?= Date: Mon, 18 Dec 2023 21:15:09 +0100 Subject: [PATCH 06/30] 0000: Fixed codestyle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- .../modbus/sungrow/internal/SungrowInverterRegisters.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java index 7da49b9b1854b..dbebba1002533 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java @@ -157,7 +157,7 @@ public enum SungrowInverterRegisters { this.channelGroup = channelGroup; } - private SungrowInverterRegisters(int registerNumber, ValueType type, float multiplier, + SungrowInverterRegisters(int registerNumber, ValueType type, float multiplier, Function stateFactory, String channelGroup) { this.multiplier = new BigDecimal(multiplier); this.registerNumber = registerNumber; From 555ca91e02c7260c081fffbb6351f4f2a9fc568a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20K=C3=BCper?= Date: Mon, 18 Dec 2023 21:15:09 +0100 Subject: [PATCH 07/30] 0000: Started documentation. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- .../README.md | 121 +++++++----------- .../doc/WiNet-S_Modbus.PNG | Bin 0 -> 67337 bytes .../doc/sungrow_logo_25_pc.png | Bin 0 -> 9006 bytes 3 files changed, 44 insertions(+), 77 deletions(-) create mode 100644 bundles/org.openhab.binding.modbus.sungrow/doc/WiNet-S_Modbus.PNG create mode 100644 bundles/org.openhab.binding.modbus.sungrow/doc/sungrow_logo_25_pc.png diff --git a/bundles/org.openhab.binding.modbus.sungrow/README.md b/bundles/org.openhab.binding.modbus.sungrow/README.md index 3dc49ad8d06a3..7b206a8bc9b64 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/README.md +++ b/bundles/org.openhab.binding.modbus.sungrow/README.md @@ -1,95 +1,62 @@ # Modbus Sungrow Binding -_Give some details about what this binding is meant for - a protocol, system, specific device._ - -_If possible, provide some resources like pictures (only PNG is supported currently), a video, etc. to give an impression of what can be done with this binding._ -_You can place such resources into a `doc` folder next to this README.md._ - -_Put each sentence in a separate line to improve readability of diffs._ +Sungrow logo + +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 said within the spec 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 -_Please describe the different supported things / devices including their ThingTypeUID within this section._ -_Which different types are supported, which models were tested etc.?_ -_Note that it is planned to generate some part of this based on the XML files within ```src/main/resources/OH-INF/thing``` of your binding._ - -- `bridge`: Short description of the Bridge, if any -- `sample`: Short description of the Thing with the ThingTypeUID `sample` - -## Discovery - -_Describe the available auto-discovery features here._ -_Mention for what it works and what needs to be kept in mind when using it._ +The binding supports only one thing: -## Binding Configuration +- `sungrowInverter`: The sungrow inverter -_If your binding requires or supports general configuration settings, please create a folder ```cfg``` and place the configuration file ```.cfg``` inside it._ -_In this section, you should link to this file and provide some information about the options._ -_The file could e.g. look like:_ +## Preparation -``` -# Configuration for the ModbusSungrow Binding -# -# Default secret key for the pairing of the ModbusSungrow Thing. -# It has to be between 10-40 (alphanumeric) characters. -# This may be changed by the user for security reasons. -secret=openHABSecret -``` +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: -_Note that it is planned to generate some part of this based on the information that is available within ```src/main/resources/OH-INF/binding``` of your binding._ +- 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 -_If your binding does not offer any generic configurations, you can remove this section completely._ +WiNet-S Modbus configuration ## Thing Configuration -_Describe what is needed to manually configure a thing, either through the UI or via a thing-file._ -_This should be mainly about its mandatory and optional configuration parameters._ +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 give the -_Note that it is planned to generate some part of this based on the XML files within ```src/main/resources/OH-INF/thing``` of your binding._ +### `sungrowInverter` Thing Configuration -### `sample` Thing Configuration - -| Name | Type | Description | Default | Required | Advanced | -|-----------------|---------|---------------------------------------|---------|----------|----------| -| hostname | text | Hostname or IP address of the device | N/A | yes | no | -| password | text | Password to access the device | N/A | yes | no | -| refreshInterval | integer | Interval the device is polled in sec. | 600 | no | yes | +| Name | Type | Description | Default | Required | Advanced | +|-----------------|---------|--------------------------------------|---------|----------|----------| +| pollInterval | integer | Interval the device is polled in ms. | 5000 | no | no | ## Channels -_Here you should provide information about available channel types, what their meaning is and how they can be used._ - -_Note that it is planned to generate some part of this based on the XML files within ```src/main/resources/OH-INF/thing``` of your binding._ - -| Channel | Type | Read/Write | Description | -|---------|--------|------------|-----------------------------| -| control | Switch | RW | This is the control channel | - -## Full Example - -_Provide a full usage example based on textual configuration files._ -_*.things, *.items examples are mandatory as textual configuration is well used by many users._ -_*.sitemap examples are optional._ - -### Thing Configuration - -```java -Example thing configuration goes here. -``` - -### Item Configuration - -```java -Example item configuration goes here. -``` - -### Sitemap Configuration - -```perl -Optional Sitemap configuration goes here. -Remove this section, if not needed. -``` - -## Any custom content here! - -_Feel free to add additional sections for whatever you think should also be mentioned about your binding!_ +The `sungrowInverter` thing has channels that serve the current state of the sungrow inverter, +as you are used to from the iSolareCloud Website and App. diff --git a/bundles/org.openhab.binding.modbus.sungrow/doc/WiNet-S_Modbus.PNG b/bundles/org.openhab.binding.modbus.sungrow/doc/WiNet-S_Modbus.PNG new file mode 100644 index 0000000000000000000000000000000000000000..942273fdc0877683f23a072b10b4034af144f853 GIT binary patch literal 67337 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 z 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 literal 0 HcmV?d00001 diff --git a/bundles/org.openhab.binding.modbus.sungrow/doc/sungrow_logo_25_pc.png b/bundles/org.openhab.binding.modbus.sungrow/doc/sungrow_logo_25_pc.png new file mode 100644 index 0000000000000000000000000000000000000000..67409f3a755b03968c2275c322cec548d46fd891 GIT binary patch literal 9006 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 U%neeo)Bpeg07*qoM6N<$f+)ygasU7T literal 0 HcmV?d00001 From 145c085f4b5e404c2b0c184f6e01ee04e51c5b0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20K=C3=BCper?= Date: Mon, 18 Dec 2023 21:15:10 +0100 Subject: [PATCH 08/30] 0000: Applied naming convention. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- .../ModbusSungrowBindingConstants.java | 2 +- .../internal/SungrowInverterHandler.java | 6 +- .../internal/SungrowInverterRegisters.java | 57 ++-- .../resources/OH-INF/thing/thing-types.xml | 290 +++++++++--------- 4 files changed, 181 insertions(+), 174 deletions(-) diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowBindingConstants.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowBindingConstants.java index c1cd6350ddad0..4c4b6e9e648c2 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowBindingConstants.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowBindingConstants.java @@ -29,5 +29,5 @@ public class ModbusSungrowBindingConstants { * ThingType-ID for Inverter. */ public static final ThingTypeUID THING_TYPE_INVERTER = new ThingTypeUID(ModbusBindingConstants.BINDING_ID, - "sungrowInverter"); + "sungrow-inverter"); } diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterHandler.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterHandler.java index 5494e7768e3cb..931095f783c8f 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterHandler.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterHandler.java @@ -175,11 +175,11 @@ private void readError(AsyncModbusFailure error) { "Failed to retrieve data: " + error.getCause().getMessage()); } - private ChannelUID createChannelUid(SungrowInverterRegisters channel) { + private ChannelUID createChannelUid(SungrowInverterRegisters register) { return new ChannelUID( // thing.getUID(), // - "sg-" + channel.getChannelGroup(), // - "sg-" + channel.name().toLowerCase() // + "sg-" + register.getChannelGroup(), // + "sg-" + register.getChannelName() // ); } } diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java index dbebba1002533..f8d66205fa972 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java @@ -78,44 +78,44 @@ public enum SungrowInverterRegisters { DAILY_PV_GENERATION(13002, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "overview"), TOTAL_PV_GENERATION(13003, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "overview"), - DAILY_EXPORT_POWER_FROM_PV(13005, UINT16, 100, quantityFactory(Units.WATT), "grid_information"), - TOTAL_EXPORT_ENERGY_FROM_PV(13006, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "grid_information"), - LOAD_POWER(13008, INT32_SWAP, 1, quantityFactory(Units.WATT), "load_information"), - EXPORT_POWER(13010, INT32_SWAP, 1, quantityFactory(Units.WATT), "grid_information"), - DAILY_BATTERY_CHARGE(13012, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery_information"), - TOTAL_BATTERY_CHARGE(13013, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery_information"), + DAILY_EXPORT_POWER_FROM_PV(13005, UINT16, 100, quantityFactory(Units.WATT), "grid-information"), + TOTAL_EXPORT_ENERGY_FROM_PV(13006, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "grid-information"), + LOAD_POWER(13008, INT32_SWAP, 1, quantityFactory(Units.WATT), "load-information"), + EXPORT_POWER(13010, INT32_SWAP, 1, quantityFactory(Units.WATT), "grid-information"), + DAILY_BATTERY_CHARGE(13012, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery-information"), + TOTAL_BATTERY_CHARGE(13013, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery-information"), /* * Not working * CO2_REDUCTION(13015, UINT32_SWAP, 0.1f, tech.units.indriya.unit.Units.KILOGRAM), */ - DAILY_DIRECT_ENERGY_CONSUMPTION(13017, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "load_information"), - TOTAL_DIRECT_ENERGY_CONSUMPTION(13018, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "load_information"), - BATTERY_VOLTAGE(13020, UINT16, 0.1f, quantityFactory(Units.VOLT), "battery_information"), - BATTERY_CURRENT(13021, UINT16, 0.1f, quantityFactory(Units.AMPERE), "battery_information"), - BATTERY_POWER(13022, UINT16, 1, quantityFactory(Units.WATT), "battery_information"), - BATTERY_LEVEL(13023, UINT16, 0.1f, PercentType::new, "battery_information"), - BATTERY_HEALTHY(13024, UINT16, 0.1f, PercentType::new, "battery_information"), + DAILY_DIRECT_ENERGY_CONSUMPTION(13017, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "load-information"), + TOTAL_DIRECT_ENERGY_CONSUMPTION(13018, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "load-information"), + BATTERY_VOLTAGE(13020, UINT16, 0.1f, quantityFactory(Units.VOLT), "battery-information"), + BATTERY_CURRENT(13021, UINT16, 0.1f, quantityFactory(Units.AMPERE), "battery-information"), + BATTERY_POWER(13022, UINT16, 1, quantityFactory(Units.WATT), "battery-information"), + BATTERY_LEVEL(13023, UINT16, 0.1f, PercentType::new, "battery-information"), + BATTERY_HEALTHY(13024, UINT16, 0.1f, PercentType::new, "battery-information"), BATTERY_TEMPERATUR(13025, INT16, 0.1f, quantityFactory(Units.KELVIN), ConversionConstants.CELSIUS_TO_KELVIN, - "battery_information"), - DAILY_BATTERY_DISCHARGE_ENERGY(13026, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery_information"), + "battery-information"), + DAILY_BATTERY_DISCHARGE_ENERGY(13026, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery-information"), TOTAL_BATTERY_DISCHARGE_ENERGY(13027, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), - "battery_information"), - SELF_CONSUMPTION_TODAY(13029, UINT16, 0.1f, PercentType::new, "load_information"), + "battery-information"), + SELF_CONSUMPTION_TODAY(13029, UINT16, 0.1f, PercentType::new, "load-information"), // Not working - // GRID_STATE(13030, UINT16, 1, quantityTypeFactory(Units.ONE, "grid_information"), + // GRID_STATE(13030, UINT16, 1, quantityTypeFactory(Units.ONE, "grid-information"), PHASE_A_CURRENT(13031, INT16, 0.1f, quantityFactory(Units.AMPERE), "overview"), PHASE_B_CURRENT(13032, INT16, 0.1f, quantityFactory(Units.AMPERE), "overview"), PHASE_C_CURRENT(13033, INT16, 0.1f, quantityFactory(Units.AMPERE), "overview"), TOTAL_ACTIVE_POWER(13034, INT32_SWAP, 1, quantityFactory(Units.WATT), "overview"), - DAILY_IMPORT_ENERGY(13036, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "grid_information"), - TOTAL_IMPORT_ENERGY(13037, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "grid_information"), - BATTERY_CAPACITY(13039, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery_information"), - DAILY_CHARGE_ENERGY(13040, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery_information"), - TOTAL_CHARGE_ENERGY(13041, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery_information"), + DAILY_IMPORT_ENERGY(13036, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "grid-information"), + TOTAL_IMPORT_ENERGY(13037, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "grid-information"), + BATTERY_CAPACITY(13039, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery-information"), + DAILY_CHARGE_ENERGY(13040, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery-information"), + TOTAL_CHARGE_ENERGY(13041, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery-information"), // DRM_STATE(13043, UINT16, 1, quantityTypeFactory(Units.ONE, "channelGroup"), - DAILY_EXPORT_ENERGY(13045, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "grid_information"), - TOTAL_EXPORT_ENERGY(13046, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "grid_information"); + DAILY_EXPORT_ENERGY(13045, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "grid-information"), + TOTAL_EXPORT_ENERGY(13046, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "grid-information"); /* * Status Registers -not known if working so not implemented yet. @@ -196,6 +196,13 @@ public String getChannelGroup() { return channelGroup; } + /** + * Returns the channel name. + */ + public String getChannelName() { + return this.name().toLowerCase().replace('_', '-'); + } + /** * Creates the {@link State} for the given register value. */ diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml index 8953594479f42..417d24de9f03e 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml @@ -4,7 +4,7 @@ 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"> - + @@ -15,10 +15,10 @@ Inverter - - - - + + + + @@ -26,84 +26,84 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + - + - - + + - - - - + + + + - - + + - - - - - - - - - - - - - + + + + + + + + + + + + + - - + + - - - - - - - + + + + + + + - - + + - - - - + + + + - + Number:Temperature - + Temperature Measurement @@ -111,9 +111,9 @@ - + Number:ElectricPotential - + Energy Measurement @@ -121,9 +121,9 @@ - + Number:ElectricCurrent - + Energy Measurement @@ -131,9 +131,9 @@ - + Number:ElectricPotential - + Energy Measurement @@ -141,9 +141,9 @@ - + Number:ElectricCurrent - + Energy Measurement @@ -151,9 +151,9 @@ - + Number:Power - + Energy Measurement @@ -161,9 +161,9 @@ - + Number:ElectricPotential - + Energy Measurement @@ -171,9 +171,9 @@ - + Number:ElectricPotential - + Energy Measurement @@ -181,9 +181,9 @@ - + Number:ElectricPotential - + Energy Measurement @@ -191,9 +191,9 @@ - + Number:Power - + Energy Measurement @@ -201,15 +201,15 @@ - + Number:Dimensionless - + Energy - + Number:Frequency - + Measurement Frequency @@ -217,9 +217,9 @@ - + Number:Energy - + Energy Measurement @@ -227,9 +227,9 @@ - + Number:Energy - + Energy Measurement @@ -237,9 +237,9 @@ - + Number:Power - + Energy Measurement @@ -247,9 +247,9 @@ - + Number:Energy - + Energy Measurement @@ -257,9 +257,9 @@ - + Number:Power - + Energy Measurement @@ -267,9 +267,9 @@ - + Number:Power - + Energy Measurement @@ -277,9 +277,9 @@ - + Number:Energy - + Energy Measurement @@ -287,9 +287,9 @@ - + Number:Energy - + Energy Measurement @@ -298,15 +298,15 @@ - + Number:Energy - + Energy Measurement @@ -314,9 +314,9 @@ - + Number:Energy - + Energy Measurement @@ -324,9 +324,9 @@ - + Number:ElectricPotential - + Energy Measurement @@ -334,9 +334,9 @@ - + Number:ElectricCurrent - + Energy Measurement @@ -344,9 +344,9 @@ - + Number:Power - + Battery Measurement @@ -354,9 +354,9 @@ - + Number:Dimensionless - + Battery Measurement @@ -364,15 +364,15 @@ - + Number:Dimensionless - + Battery - + Number:Temperature - + Battery Measurement @@ -380,9 +380,9 @@ - + Number:Energy - + Energy Measurement @@ -390,9 +390,9 @@ - + Number:Energy - + Energy Measurement @@ -400,21 +400,21 @@ - + Number:Dimensionless - + - + Number:Dimensionless - + --> - + Number:ElectricCurrent - + Energy Measurement @@ -422,9 +422,9 @@ - + Number:ElectricCurrent - + Energy Measurement @@ -432,9 +432,9 @@ - + Number:ElectricCurrent - + Energy Measurement @@ -442,9 +442,9 @@ - + Number:Power - + Energy Measurement @@ -452,9 +452,9 @@ - + Number:Energy - + Energy Measurement @@ -462,9 +462,9 @@ - + Number:Energy - + Energy Measurement @@ -472,9 +472,9 @@ - + Number:Energy - + Energy Measurement @@ -482,9 +482,9 @@ - + Number:Energy - + Energy Measurement @@ -492,9 +492,9 @@ - + Number:Energy - + Energy Measurement @@ -503,9 +503,9 @@ - + Number:Energy - + Energy Measurement @@ -513,9 +513,9 @@ - + Number:Energy - + Energy Measurement From da69ef3470b0c40eb1ce752e4f4cde8fb15c3f7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20K=C3=BCper?= Date: Mon, 18 Dec 2023 21:15:11 +0100 Subject: [PATCH 09/30] 0000: Adjusted Labels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- .../resources/OH-INF/thing/thing-types.xml | 97 ++++++++++--------- 1 file changed, 49 insertions(+), 48 deletions(-) diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml index 417d24de9f03e..a02f48bf2d6f8 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml @@ -10,7 +10,7 @@ - + Sungrow inverter connected via Modbus. Inverter @@ -24,7 +24,7 @@ - + @@ -47,7 +47,7 @@ - + @@ -57,7 +57,8 @@ - + + Battery @@ -76,7 +77,7 @@ - + @@ -92,7 +93,7 @@ - + @@ -103,7 +104,7 @@ Number:Temperature - + Temperature Measurement @@ -113,7 +114,7 @@ Number:ElectricPotential - + Energy Measurement @@ -123,7 +124,7 @@ Number:ElectricCurrent - + Energy Measurement @@ -133,7 +134,7 @@ Number:ElectricPotential - + Energy Measurement @@ -143,7 +144,7 @@ Number:ElectricCurrent - + Energy Measurement @@ -153,7 +154,7 @@ Number:Power - + Energy Measurement @@ -163,7 +164,7 @@ Number:ElectricPotential - + Energy Measurement @@ -173,7 +174,7 @@ Number:ElectricPotential - + Energy Measurement @@ -183,7 +184,7 @@ Number:ElectricPotential - + Energy Measurement @@ -193,7 +194,7 @@ Number:Power - + Energy Measurement @@ -203,13 +204,13 @@ Number:Dimensionless - + Energy Number:Frequency - + Measurement Frequency @@ -219,7 +220,7 @@ Number:Energy - + Energy Measurement @@ -229,7 +230,7 @@ Number:Energy - + Energy Measurement @@ -239,7 +240,7 @@ Number:Power - + Energy Measurement @@ -249,7 +250,7 @@ Number:Energy - + Energy Measurement @@ -259,7 +260,7 @@ Number:Power - + Energy Measurement @@ -269,7 +270,7 @@ Number:Power - + Energy Measurement @@ -279,7 +280,7 @@ Number:Energy - + Energy Measurement @@ -289,7 +290,7 @@ Number:Energy - + Energy Measurement @@ -306,7 +307,7 @@ --> Number:Energy - + Energy Measurement @@ -316,7 +317,7 @@ Number:Energy - + Energy Measurement @@ -326,7 +327,7 @@ Number:ElectricPotential - + Energy Measurement @@ -336,7 +337,7 @@ Number:ElectricCurrent - + Energy Measurement @@ -346,7 +347,7 @@ Number:Power - + Battery Measurement @@ -356,7 +357,7 @@ Number:Dimensionless - + Battery Measurement @@ -366,13 +367,13 @@ Number:Dimensionless - + Battery Number:Temperature - + Battery Measurement @@ -382,7 +383,7 @@ Number:Energy - + Energy Measurement @@ -392,7 +393,7 @@ Number:Energy - + Energy Measurement @@ -402,7 +403,7 @@ Number:Dimensionless - + @@ -414,7 +415,7 @@ --> Number:ElectricCurrent - + Energy Measurement @@ -424,7 +425,7 @@ Number:ElectricCurrent - + Energy Measurement @@ -434,7 +435,7 @@ Number:ElectricCurrent - + Energy Measurement @@ -444,7 +445,7 @@ Number:Power - + Energy Measurement @@ -454,7 +455,7 @@ Number:Energy - + Energy Measurement @@ -464,7 +465,7 @@ Number:Energy - + Energy Measurement @@ -474,7 +475,7 @@ Number:Energy - + Energy Measurement @@ -484,7 +485,7 @@ Number:Energy - + Energy Measurement @@ -494,7 +495,7 @@ Number:Energy - + Energy Measurement @@ -505,7 +506,7 @@ Number:Energy - + Energy Measurement @@ -515,7 +516,7 @@ Number:Energy - + Energy Measurement From 904047acef613a786c6e6afb1032760b3e0be2a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20K=C3=BCper?= Date: Mon, 18 Dec 2023 21:15:11 +0100 Subject: [PATCH 10/30] 0000: Typos fixed. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- .../sungrow/internal/SungrowInverterRegisters.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java index f8d66205fa972..3b9b1da094f9e 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java @@ -45,10 +45,10 @@ public enum SungrowInverterRegisters { INTERNAL_TEMPERATURE(5008, INT16, 0.1f, quantityFactory(Units.KELVIN), ConversionConstants.CELSIUS_TO_KELVIN, "overview"), - MPPT1_VOLTAGE(5011, UINT16, 0.1f, quantityFactory(Units.VOLT), "mppt_information"), - MPPT1_CURRENT(5012, UINT16, 0.1f, quantityFactory(Units.AMPERE), "mppt_information"), - MPPT2_VOLTAGE(5013, UINT16, 0.1f, quantityFactory(Units.VOLT), "mppt_information"), - MPPT2_CURRENT(5014, UINT16, 0.1f, quantityFactory(Units.AMPERE), "mppt_information"), + MPPT1_VOLTAGE(5011, UINT16, 0.1f, quantityFactory(Units.VOLT), "mppt-information"), + MPPT1_CURRENT(5012, UINT16, 0.1f, quantityFactory(Units.AMPERE), "mppt-information"), + MPPT2_VOLTAGE(5013, UINT16, 0.1f, quantityFactory(Units.VOLT), "mppt-information"), + MPPT2_CURRENT(5014, UINT16, 0.1f, quantityFactory(Units.AMPERE), "mppt-information"), TOTAL_DC_POWER(5017, UINT32_SWAP, 1, quantityFactory(Units.WATT), "overview"), PHASE_A_VOLTAGE(5019, UINT16, 0.1f, quantityFactory(Units.VOLT), "overview"), PHASE_B_VOLTAGE(5020, UINT16, 0.1f, quantityFactory(Units.VOLT), "overview"), @@ -95,7 +95,7 @@ public enum SungrowInverterRegisters { BATTERY_POWER(13022, UINT16, 1, quantityFactory(Units.WATT), "battery-information"), BATTERY_LEVEL(13023, UINT16, 0.1f, PercentType::new, "battery-information"), BATTERY_HEALTHY(13024, UINT16, 0.1f, PercentType::new, "battery-information"), - BATTERY_TEMPERATUR(13025, INT16, 0.1f, quantityFactory(Units.KELVIN), ConversionConstants.CELSIUS_TO_KELVIN, + BATTERY_TEMPERATURE(13025, INT16, 0.1f, quantityFactory(Units.KELVIN), ConversionConstants.CELSIUS_TO_KELVIN, "battery-information"), DAILY_BATTERY_DISCHARGE_ENERGY(13026, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery-information"), TOTAL_BATTERY_DISCHARGE_ENERGY(13027, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), From 5cb7901a2fbb35f2a0ce5bc47115a3ba05b085b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20K=C3=BCper?= Date: Mon, 18 Dec 2023 21:15:12 +0100 Subject: [PATCH 11/30] 0000: Changed multiplicand to BigInteger, so values are more accurate (instead of floats issues). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- .../sungrow/internal/ConversionConstants.java | 6 + .../internal/SungrowInverterRegisters.java | 122 ++++++++++-------- .../SungrowInverterRegistersTest.java | 73 +++++++++++ 3 files changed, 149 insertions(+), 52 deletions(-) create mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/test/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersTest.java diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ConversionConstants.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ConversionConstants.java index 396bff31a5d67..149e25005aea6 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ConversionConstants.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ConversionConstants.java @@ -13,6 +13,7 @@ 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; @@ -28,6 +29,11 @@ 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. */ diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java index 3b9b1da094f9e..d971fb3354ab1 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java @@ -18,6 +18,7 @@ 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; @@ -40,23 +41,23 @@ 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, 0.1f, quantityTypeFactory(Units.KILOWATT_HOUR), - // TOTAL_OUTPUT_ENERGY(5004, UINT32_SWAP, 0.1f, quantityTypeFactory(Units.KILOWATT_HOUR), - - INTERNAL_TEMPERATURE(5008, INT16, 0.1f, quantityFactory(Units.KELVIN), ConversionConstants.CELSIUS_TO_KELVIN, - "overview"), - MPPT1_VOLTAGE(5011, UINT16, 0.1f, quantityFactory(Units.VOLT), "mppt-information"), - MPPT1_CURRENT(5012, UINT16, 0.1f, quantityFactory(Units.AMPERE), "mppt-information"), - MPPT2_VOLTAGE(5013, UINT16, 0.1f, quantityFactory(Units.VOLT), "mppt-information"), - MPPT2_CURRENT(5014, UINT16, 0.1f, quantityFactory(Units.AMPERE), "mppt-information"), - TOTAL_DC_POWER(5017, UINT32_SWAP, 1, quantityFactory(Units.WATT), "overview"), - PHASE_A_VOLTAGE(5019, UINT16, 0.1f, quantityFactory(Units.VOLT), "overview"), - PHASE_B_VOLTAGE(5020, UINT16, 0.1f, quantityFactory(Units.VOLT), "overview"), - PHASE_C_VOLTAGE(5021, UINT16, 0.1f, quantityFactory(Units.VOLT), "overview"), - - REACTIVE_POWER(5033, INT32_SWAP, 1, quantityFactory(Units.VAR), "overview"), - POWER_FACTOR(5035, INT16, 0.001f, DecimalType::new, "overview"), - GRID_FREQUENCY(5036, UINT16, 0.01f, quantityFactory(Units.HERTZ), "overview"), + // 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 @@ -76,46 +77,62 @@ public enum SungrowInverterRegisters { * RUNNING_STATE(13001, UINT16, 1, quantityTypeFactory(Units.ONE)), */ - DAILY_PV_GENERATION(13002, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "overview"), - TOTAL_PV_GENERATION(13003, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "overview"), - DAILY_EXPORT_POWER_FROM_PV(13005, UINT16, 100, quantityFactory(Units.WATT), "grid-information"), - TOTAL_EXPORT_ENERGY_FROM_PV(13006, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "grid-information"), - LOAD_POWER(13008, INT32_SWAP, 1, quantityFactory(Units.WATT), "load-information"), - EXPORT_POWER(13010, INT32_SWAP, 1, quantityFactory(Units.WATT), "grid-information"), - DAILY_BATTERY_CHARGE(13012, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery-information"), - TOTAL_BATTERY_CHARGE(13013, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery-information"), + 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_POWER_FROM_PV(13005, UINT16, BigDecimal.valueOf(100), quantityFactory(Units.WATT), "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, 0.1f, tech.units.indriya.unit.Units.KILOGRAM), + * CO2_REDUCTION(13015, UINT32_SWAP, ConversionConstants.DIV_BY_TEN, tech.units.indriya.unit.Units.KILOGRAM), */ - DAILY_DIRECT_ENERGY_CONSUMPTION(13017, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "load-information"), - TOTAL_DIRECT_ENERGY_CONSUMPTION(13018, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "load-information"), - BATTERY_VOLTAGE(13020, UINT16, 0.1f, quantityFactory(Units.VOLT), "battery-information"), - BATTERY_CURRENT(13021, UINT16, 0.1f, quantityFactory(Units.AMPERE), "battery-information"), - BATTERY_POWER(13022, UINT16, 1, quantityFactory(Units.WATT), "battery-information"), - BATTERY_LEVEL(13023, UINT16, 0.1f, PercentType::new, "battery-information"), - BATTERY_HEALTHY(13024, UINT16, 0.1f, PercentType::new, "battery-information"), - BATTERY_TEMPERATURE(13025, INT16, 0.1f, quantityFactory(Units.KELVIN), ConversionConstants.CELSIUS_TO_KELVIN, + 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"), - DAILY_BATTERY_DISCHARGE_ENERGY(13026, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery-information"), - TOTAL_BATTERY_DISCHARGE_ENERGY(13027, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), + 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"), - SELF_CONSUMPTION_TODAY(13029, UINT16, 0.1f, PercentType::new, "load-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, 0.1f, quantityFactory(Units.AMPERE), "overview"), - PHASE_B_CURRENT(13032, INT16, 0.1f, quantityFactory(Units.AMPERE), "overview"), - PHASE_C_CURRENT(13033, INT16, 0.1f, quantityFactory(Units.AMPERE), "overview"), - TOTAL_ACTIVE_POWER(13034, INT32_SWAP, 1, quantityFactory(Units.WATT), "overview"), - DAILY_IMPORT_ENERGY(13036, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "grid-information"), - TOTAL_IMPORT_ENERGY(13037, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "grid-information"), - BATTERY_CAPACITY(13039, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery-information"), - DAILY_CHARGE_ENERGY(13040, UINT16, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery-information"), - TOTAL_CHARGE_ENERGY(13041, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "battery-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, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "grid-information"), - TOTAL_EXPORT_ENERGY(13046, UINT32_SWAP, 0.1f, quantityFactory(Units.KILOWATT_HOUR), "grid-information"); + 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. @@ -146,10 +163,10 @@ public enum SungrowInverterRegisters { private final Function stateFactory; private final String channelGroup; - SungrowInverterRegisters(int registerNumber, ValueType type, float multiplier, + SungrowInverterRegisters(int registerNumber, ValueType type, BigDecimal multiplier, Function stateFactory, Function conversion, String channelGroup) { - this.multiplier = new BigDecimal(multiplier); + this.multiplier = multiplier; this.registerNumber = registerNumber; this.type = type; this.conversion = conversion; @@ -157,9 +174,9 @@ public enum SungrowInverterRegisters { this.channelGroup = channelGroup; } - SungrowInverterRegisters(int registerNumber, ValueType type, float multiplier, + SungrowInverterRegisters(int registerNumber, ValueType type, BigDecimal multiplier, Function stateFactory, String channelGroup) { - this.multiplier = new BigDecimal(multiplier); + this.multiplier = multiplier; this.registerNumber = registerNumber; this.type = type; this.conversion = Function.identity(); @@ -208,6 +225,7 @@ public String getChannelName() { */ public State createState(DecimalType registerValue) { final BigDecimal scaledValue = registerValue.toBigDecimal().multiply(this.multiplier); + final BigDecimal convertedValue = conversion.apply(scaledValue); return this.stateFactory.apply(convertedValue); } diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/test/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersTest.java b/bundles/org.openhab.binding.modbus.sungrow/src/test/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersTest.java new file mode 100644 index 0000000000000..c67b374a10056 --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sungrow/src/test/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersTest.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2010-2022 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 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 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()); + } +} From 219a37a9f521e47cb128eb734acdeac2ef1e0784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20K=C3=BCper?= Date: Mon, 18 Dec 2023 21:15:13 +0100 Subject: [PATCH 12/30] 0000: Adjusted copyright MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- .../modbus/sungrow/internal/SungrowInverterRegistersTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/test/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersTest.java b/bundles/org.openhab.binding.modbus.sungrow/src/test/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersTest.java index c67b374a10056..0c581c17f75ea 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/test/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersTest.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/test/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersTest.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2022 Contributors to the openHAB project + * Copyright (c) 2010-2023 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. From b5479d6d9430b5da7c30d6a3c2c804497d3fb8bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20K=C3=BCper?= Date: Mon, 18 Dec 2023 21:15:13 +0100 Subject: [PATCH 13/30] 0000: Fixed CODEOWNERS file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 77d9a049371b8..d8f726f03add5 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -216,7 +216,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@gmx.de +/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 From 53b8f0cf1036a464c7c07d7b784cbddc8d85e792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20K=C3=BCper?= Date: Mon, 18 Dec 2023 21:15:14 +0100 Subject: [PATCH 14/30] 0000: Added configuration example in documentation. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- .../README.md | 45 ++++++++++++++++++- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.modbus.sungrow/README.md b/bundles/org.openhab.binding.modbus.sungrow/README.md index 7b206a8bc9b64..3ec2cbad8922e 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/README.md +++ b/bundles/org.openhab.binding.modbus.sungrow/README.md @@ -8,7 +8,7 @@ which can be found here: https://github.com/bohdan-s/SunGather/issues/36. ## Supported inverters -As said within the spec the following inverters are supported (but not all are tested yet): +As defined within the spec mentioned above the following inverters are supported, but not all are tested yet: - SH3K6 - SH4K6 @@ -43,12 +43,13 @@ If you are using a Modbus TCP Slave and the WiNet-S Communication Module please - 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: 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 give the +You just have to select the configured bridge and optional configure the polling interval. ### `sungrowInverter` Thing Configuration @@ -60,3 +61,43 @@ You just have to select the configured bridge and give the The `sungrowInverter` thing has channels that serve the current state of the sungrow inverter, as you are used to from the iSolareCloud Website and App. + +## Configuration 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 modbus:sungrow-inverter:sungrowBridge: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"] {modbus:sungrow-inverter:sungrowBridge:sungrowInverter:sg-battery-information#sg-battery-power} +Number:Dimensionless battery_level "Battery Level" (batteryInformation) ["Measurement", "Energy"] {modbus:sungrow-inverter:sungrowBridge:sungrowInverter:sg-battery-information#sg-battery-power} +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"} + +// 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"} +``` \ No newline at end of file From a3d5f070ef0345a95c06a884e6ab76ce09218993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20K=C3=BCper?= Date: Mon, 18 Dec 2023 21:15:15 +0100 Subject: [PATCH 15/30] 0000: corrected factor and unit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- .../modbus/sungrow/internal/SungrowInverterRegisters.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java index d971fb3354ab1..f18787e67b189 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java @@ -81,7 +81,7 @@ public enum SungrowInverterRegisters { "overview"), TOTAL_PV_GENERATION(13003, UINT32_SWAP, ConversionConstants.DIV_BY_TEN, quantityFactory(Units.KILOWATT_HOUR), "overview"), - DAILY_EXPORT_POWER_FROM_PV(13005, UINT16, BigDecimal.valueOf(100), quantityFactory(Units.WATT), "grid-information"), + 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"), From 17e13edb4dc186cac43b129e98a97fccdbe9baf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20K=C3=BCper?= Date: Mon, 18 Dec 2023 21:15:15 +0100 Subject: [PATCH 16/30] Update bundles/org.openhab.binding.modbus.sungrow/pom.xml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wouter Born Signed-off-by: Sönke Küper --- bundles/org.openhab.binding.modbus.sungrow/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.modbus.sungrow/pom.xml b/bundles/org.openhab.binding.modbus.sungrow/pom.xml index a9be18c4c86e0..63a4caf00a49e 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/pom.xml +++ b/bundles/org.openhab.binding.modbus.sungrow/pom.xml @@ -7,7 +7,7 @@ org.openhab.addons.bundles org.openhab.addons.reactor.bundles - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT org.openhab.binding.modbus.sungrow From d85b8c4a5c82158e185939851fd63d9eaf50e9a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20K=C3=BCper?= Date: Mon, 18 Dec 2023 21:15:16 +0100 Subject: [PATCH 17/30] 0000: spotless-fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- .../modbus/sungrow/internal/SungrowInverterRegisters.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java index f18787e67b189..4db27846cc901 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java @@ -81,7 +81,8 @@ public enum SungrowInverterRegisters { "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"), + 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"), From 7393e38c9f39f30b3d6173663f2b4f49ff96ce54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20K=C3=BCper?= Date: Mon, 18 Dec 2023 21:15:17 +0100 Subject: [PATCH 18/30] 0000: checkstyle-fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- bundles/org.openhab.binding.modbus.sungrow/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.modbus.sungrow/README.md b/bundles/org.openhab.binding.modbus.sungrow/README.md index 3ec2cbad8922e..e9c41661e2ac7 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/README.md +++ b/bundles/org.openhab.binding.modbus.sungrow/README.md @@ -63,9 +63,11 @@ The `sungrowInverter` thing has channels that serve the current state of the sun as you are used to from the iSolareCloud Website and App. ## Configuration 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 modbus:sungrow-inverter:sungrowBridge:sungrowInverter "Sungrow Inverter" [ pollInterval=5000 ] @@ -73,6 +75,7 @@ Bridge modbus:tcp:sungrowBridge [ host="10.0.0.2", port=502, id=1, enableDiscove ``` sungrow.items + ```java // Groups Group sungrowInverter "Sungrow Inverter" ["Inverter"] @@ -100,4 +103,4 @@ Number:Energy daily_export_energy "Daily Export Energy" (gridInformation) ["Meas // 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"} -``` \ No newline at end of file +``` From 6c4b7d11262a0f585e670855c2c209d393981c35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20K=C3=BCper?= Date: Sat, 23 Dec 2023 18:43:14 +0100 Subject: [PATCH 19/30] Update bundles/org.openhab.binding.modbus.sungrow/pom.xml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wouter Born Signed-off-by: Sönke Küper --- bundles/org.openhab.binding.modbus.sungrow/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.modbus.sungrow/pom.xml b/bundles/org.openhab.binding.modbus.sungrow/pom.xml index 63a4caf00a49e..63dbb502dd4a5 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/pom.xml +++ b/bundles/org.openhab.binding.modbus.sungrow/pom.xml @@ -7,7 +7,7 @@ org.openhab.addons.bundles org.openhab.addons.reactor.bundles - 4.1.0-SNAPSHOT + 4.2.0-SNAPSHOT org.openhab.binding.modbus.sungrow From c31eb391737993ccd9392ac323ed52f8fd99a7d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20K=C3=BCper?= Date: Mon, 1 Jan 2024 19:16:36 +0100 Subject: [PATCH 20/30] Updated licence headers. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- .../binding/modbus/sungrow/internal/ConversionConstants.java | 2 +- .../modbus/sungrow/internal/ModbusSungrowBindingConstants.java | 2 +- .../modbus/sungrow/internal/ModbusSungrowHandlerFactory.java | 2 +- .../modbus/sungrow/internal/SungrowInverterConfiguration.java | 2 +- .../binding/modbus/sungrow/internal/SungrowInverterHandler.java | 2 +- .../modbus/sungrow/internal/SungrowInverterRegisters.java | 2 +- .../modbus/sungrow/internal/SungrowInverterRegistersTest.java | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ConversionConstants.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ConversionConstants.java index 149e25005aea6..9d7298bd317fc 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ConversionConstants.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ConversionConstants.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowBindingConstants.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowBindingConstants.java index 4c4b6e9e648c2..1b1abaeacb3e5 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowBindingConstants.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowBindingConstants.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowHandlerFactory.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowHandlerFactory.java index a6a7a9c03855f..d0a547cd18022 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowHandlerFactory.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/ModbusSungrowHandlerFactory.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterConfiguration.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterConfiguration.java index c58103b77e1ba..88965f9c52024 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterConfiguration.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterConfiguration.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterHandler.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterHandler.java index 931095f783c8f..e4744a29eb1d9 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterHandler.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java index 4db27846cc901..00d5720e3cf6f 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/test/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersTest.java b/bundles/org.openhab.binding.modbus.sungrow/src/test/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersTest.java index 0c581c17f75ea..422bcf9954c08 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/test/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersTest.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/test/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegistersTest.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2023 Contributors to the openHAB project + * Copyright (c) 2010-2024 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. From 03d875dd74477d300dfd5c80d40f19bf8a813fe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20K=C3=BCper?= Date: Mon, 1 Jan 2024 19:30:32 +0100 Subject: [PATCH 21/30] Applied review remarks. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- .../README.md | 8 +-- .../doc/sungrow_logo_25_pc.png | Bin 9006 -> 0 bytes .../internal/SungrowInverterHandler.java | 2 +- .../OH-INF/i18n/sungrow_de.properties | 64 ------------------ .../resources/OH-INF/thing/thing-types.xml | 20 ------ 5 files changed, 4 insertions(+), 90 deletions(-) delete mode 100644 bundles/org.openhab.binding.modbus.sungrow/doc/sungrow_logo_25_pc.png delete mode 100644 bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/i18n/sungrow_de.properties diff --git a/bundles/org.openhab.binding.modbus.sungrow/README.md b/bundles/org.openhab.binding.modbus.sungrow/README.md index e9c41661e2ac7..118eaa60d8381 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/README.md +++ b/bundles/org.openhab.binding.modbus.sungrow/README.md @@ -1,7 +1,5 @@ # Modbus Sungrow Binding -Sungrow logo - 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. @@ -53,9 +51,9 @@ You just have to select the configured bridge and optional configure the polling ### `sungrowInverter` Thing Configuration -| Name | Type | Description | Default | Required | Advanced | -|-----------------|---------|--------------------------------------|---------|----------|----------| -| pollInterval | integer | Interval the device is polled in ms. | 5000 | no | no | +| Name | Type | Description | Default | Required | Advanced | +|--------------|---------|--------------------------------------|---------|----------|----------| +| pollInterval | integer | Interval the device is polled in ms. | 5000 | yes | no | ## Channels diff --git a/bundles/org.openhab.binding.modbus.sungrow/doc/sungrow_logo_25_pc.png b/bundles/org.openhab.binding.modbus.sungrow/doc/sungrow_logo_25_pc.png deleted file mode 100644 index 67409f3a755b03968c2275c322cec548d46fd891..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9006 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 U%neeo)Bpeg07*qoM6N<$f+)ygasU7T diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterHandler.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterHandler.java index e4744a29eb1d9..afcaa81d38ce9 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterHandler.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterHandler.java @@ -170,7 +170,7 @@ private void readSuccessful(ModbusRequest request, AsyncModbusReadResult result) } private void readError(AsyncModbusFailure error) { - this.logger.error("Failed to get modbus data", error.getCause()); + this.logger.debug("Failed to get modbus data", error.getCause()); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Failed to retrieve data: " + error.getCause().getMessage()); } diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/i18n/sungrow_de.properties b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/i18n/sungrow_de.properties deleted file mode 100644 index 5b2df586ec129..0000000000000 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/i18n/sungrow_de.properties +++ /dev/null @@ -1,64 +0,0 @@ -# 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 = Überblick -channel-group-type.modbus.sg-mppt_information.label = MPPT -channel-group-type.modbus.sg-battery_information.label = Batterie -channel-group-type.modbus.sg-load_information.label = Last -channel-group-type.modbus.sg-grid_information.label = Stromnetz - -# channel types - -channel-type.modbus.sg-battery_capacity.label = Batteriekapazität -channel-type.modbus.sg-battery_current.label = Batteriestrom -channel-type.modbus.sg-battery_healthy.label = Gesundheit der Batterie (SOH) -channel-type.modbus.sg-battery_level.label = Batteriestand -channel-type.modbus.sg-battery_power.label = Batterieleistung -channel-type.modbus.sg-battery_temperature.label = Batterietemperatur -channel-type.modbus.sg-battery_voltage.label = Batteriespannung -channel-type.modbus.sg-co2_reduction.label = Co2 Einsparung -channel-type.modbus.sg-daily_battery_charge.label = Eingespeicherte PV-Energie (Tagesbasis) -channel-type.modbus.sg-daily_battery_discharge_energy.label = Aus Batterie entnommen (Tagesbasis) -channel-type.modbus.sg-daily_charge_energy.label = In Batterie gespeichert (Tagesbasis) -channel-type.modbus.sg-daily_direct_energy_consumption.label = Tages-PV-Eigenverbrauch -channel-type.modbus.sg-daily_export_energy.label = Tägliche Netzeinspeisung -channel-type.modbus.sg-daily_export_power_from_pv.label = Tägliches PV-Einspeisenetzvolumen -channel-type.modbus.sg-daily_import_energy.label = Tägliche Energie abgenommen vom Netz -channel-type.modbus.sg-daily_pv_generation.label = Tägliche PV-Stromerzeugung -channel-type.modbus.sg-export_power.label = Wirkleistung Einspeisung -channel-type.modbus.sg-grid_frequency.label = Netzfrequenz -channel-type.modbus.sg-internal_temperature.label = Temperatur -channel-type.modbus.sg-load_power.label = Lastleistung -channel-type.modbus.sg-mppt1_current.label = MPPT1 Strom -channel-type.modbus.sg-mppt1_voltage.label = MPPT1 Spannung -channel-type.modbus.sg-mppt2_current.label = MPPT2 Strom -channel-type.modbus.sg-mppt2_voltage.label = MPPT2 Spannung -channel-type.modbus.sg-phase_a_current.label = Strom Phase A -channel-type.modbus.sg-phase_a_voltage.label = Spannung Phase A -channel-type.modbus.sg-phase_b_current.label = Strom Phase B -channel-type.modbus.sg-phase_b_voltage.label = Spannung Phase B -channel-type.modbus.sg-phase_c_current.label = Strom Phase C -channel-type.modbus.sg-phase_c_voltage.label = Spannung Phase C -channel-type.modbus.sg-power_factor.label = Leistungsfaktor -channel-type.modbus.sg-reactive_power.label = Blindleistung -channel-type.modbus.sg-running_state.label = Betriebsstatus -channel-type.modbus.sg-self_consumption_today.label = Tägliche Eigenverbrauchsrate -channel-type.modbus.sg-system_state.label = Aktueller Zustand -channel-type.modbus.sg-total_active_power.label = Wirkleistung Gesamt -channel-type.modbus.sg-total_battery_charge.label = Eingespeicherte PV-Energie (Gesamt) -channel-type.modbus.sg-total_battery_discharge_energy.label = Aus Batterie entnommen (Gesamt) -channel-type.modbus.sg-total_charge_energy.label = In Batterie gespeichert (Gesamt) -channel-type.modbus.sg-total_dc_power.label = PV-Leistung -channel-type.modbus.sg-total_direct_energy_consumption.label = Gesamter PV-Eigenverbrauch -channel-type.modbus.sg-total_export_energy.label = Netzeinspeisung gesamt -channel-type.modbus.sg-total_export_energy_from_pv.label = Gesamt-Verlauf PV Einspeisemenge -channel-type.modbus.sg-total_import_energy.label = Netzbezug insgesamt -channel-type.modbus.sg-total_pv_generation.label = Gesamte PV-Stromerzeugung diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml index a02f48bf2d6f8..127c10bdd6852 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/resources/OH-INF/thing/thing-types.xml @@ -39,9 +39,6 @@ - @@ -86,9 +83,6 @@ - @@ -298,13 +292,6 @@ - Number:Energy @@ -406,13 +393,6 @@ - - - Number:Dimensionless - - - - --> Number:ElectricCurrent From a5f8b8db359389da1b1144b13e192172971c14fc Mon Sep 17 00:00:00 2001 From: Eric Bodden Date: Wed, 3 Jan 2024 17:40:39 +0100 Subject: [PATCH 22/30] Update README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- bundles/org.openhab.binding.modbus.sungrow/README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/bundles/org.openhab.binding.modbus.sungrow/README.md b/bundles/org.openhab.binding.modbus.sungrow/README.md index 118eaa60d8381..42fea0e208dd3 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/README.md +++ b/bundles/org.openhab.binding.modbus.sungrow/README.md @@ -68,7 +68,7 @@ sungrow.things ```java Bridge modbus:tcp:sungrowBridge [ host="10.0.0.2", port=502, id=1, enableDiscovery=false ] { - Thing modbus:sungrow-inverter:sungrowBridge:sungrowInverter "Sungrow Inverter" [ pollInterval=5000 ] + Thing sungrow-inverter sungrowInverter "Sungrow Inverter" [ pollInterval=5000 ] } ``` @@ -89,8 +89,8 @@ Number:Energy daily_pv_generation "Daily PV Generation" (overview) ["Measurement 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"] {modbus:sungrow-inverter:sungrowBridge:sungrowInverter:sg-battery-information#sg-battery-power} -Number:Dimensionless battery_level "Battery Level" (batteryInformation) ["Measurement", "Energy"] {modbus:sungrow-inverter:sungrowBridge:sungrowInverter:sg-battery-information#sg-battery-power} +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-power", stateDescription=""[pattern="%.1f W"]} 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"} @@ -100,5 +100,4 @@ Number:Energy daily_export_energy "Daily Export Energy" (gridInformation) ["Meas // 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"} -``` +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"}``` From 9b3ff27ae2bb3ef3132afd2b79cb577f61a3680b Mon Sep 17 00:00:00 2001 From: Eric Bodden Date: Wed, 3 Jan 2024 17:42:34 +0100 Subject: [PATCH 23/30] Update README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- bundles/org.openhab.binding.modbus.sungrow/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.modbus.sungrow/README.md b/bundles/org.openhab.binding.modbus.sungrow/README.md index 42fea0e208dd3..01f8f4d7e17f1 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/README.md +++ b/bundles/org.openhab.binding.modbus.sungrow/README.md @@ -90,7 +90,7 @@ Number:Energy total_pv_generation "Total PV Generation" (overview) ["Measurement // 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-power", stateDescription=""[pattern="%.1f W"]} +Number:Dimensionless battery_level "Battery Level" (batteryInformation) ["Measurement", "Energy"] {channel="modbus:sungrow-inverter:sungrowBridge:sungrowInverter:sg-battery-information#sg-battery-power"} 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"} From bb27ff2d8deeec925ac881d67d29dfb3ab9195ac Mon Sep 17 00:00:00 2001 From: Eric Bodden Date: Thu, 4 Jan 2024 15:37:48 +0100 Subject: [PATCH 24/30] Fixed type label, must be Number:Power for battery_level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- bundles/org.openhab.binding.modbus.sungrow/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.modbus.sungrow/README.md b/bundles/org.openhab.binding.modbus.sungrow/README.md index 01f8f4d7e17f1..c88545b7c5f60 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/README.md +++ b/bundles/org.openhab.binding.modbus.sungrow/README.md @@ -90,7 +90,7 @@ Number:Energy total_pv_generation "Total PV Generation" (overview) ["Measurement // 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-power"} +Number:Power battery_level "Battery Level" (batteryInformation) ["Measurement", "Energy"] {channel="modbus:sungrow-inverter:sungrowBridge:sungrowInverter:sg-battery-information#sg-battery-power"} 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"} From 7b563ec2079a6142b726c2c2a919cfcdd2b36c61 Mon Sep 17 00:00:00 2001 From: Eric Bodden Date: Fri, 5 Jan 2024 14:12:37 +0100 Subject: [PATCH 25/30] Corrected fix for battery_level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- bundles/org.openhab.binding.modbus.sungrow/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.modbus.sungrow/README.md b/bundles/org.openhab.binding.modbus.sungrow/README.md index c88545b7c5f60..6a6a8b8548c2d 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/README.md +++ b/bundles/org.openhab.binding.modbus.sungrow/README.md @@ -90,7 +90,7 @@ Number:Energy total_pv_generation "Total PV Generation" (overview) ["Measurement // Battery information Number:Power battery_power "Battery Power" (batteryInformation) ["Measurement", "Power"] {channel="modbus:sungrow-inverter:sungrowBridge:sungrowInverter:sg-battery-information#sg-battery-power"} -Number:Power battery_level "Battery Level" (batteryInformation) ["Measurement", "Energy"] {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"} From 8301aabef28c56f4e92abbde19fb6636b1b1ab07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20K=C3=BCper?= Date: Sat, 13 Jan 2024 20:10:06 +0100 Subject: [PATCH 26/30] Added daily import energy in config example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- bundles/org.openhab.binding.modbus.sungrow/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/bundles/org.openhab.binding.modbus.sungrow/README.md b/bundles/org.openhab.binding.modbus.sungrow/README.md index 6a6a8b8548c2d..1102a52ea3a8a 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/README.md +++ b/bundles/org.openhab.binding.modbus.sungrow/README.md @@ -97,6 +97,7 @@ Number:Energy daily_discharge_energy "Daily Battery Discharge Energy" (batteryIn // 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"} From b6e8e1eec037929ace560c5411b1ec50263cd3be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20K=C3=BCper?= Date: Sat, 13 Jan 2024 20:40:36 +0100 Subject: [PATCH 27/30] Added Channel Descriptions. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- .../README.md | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/bundles/org.openhab.binding.modbus.sungrow/README.md b/bundles/org.openhab.binding.modbus.sungrow/README.md index 1102a52ea3a8a..fa85cdce9ab68 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/README.md +++ b/bundles/org.openhab.binding.modbus.sungrow/README.md @@ -60,6 +60,51 @@ You just have to select the configured bridge and optional configure the polling The `sungrowInverter` 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 | + ## Configuration Example This example shows how to configure a sungrow inverter connected via modbus and uses the most common channels. From 69b74c291fd6fc4aeaaa33ce4b07bae1f58983b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20K=C3=BCper?= Date: Sat, 13 Jan 2024 20:47:32 +0100 Subject: [PATCH 28/30] Added javadocs. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- .../internal/SungrowInverterHandler.java | 2 +- .../internal/SungrowInverterRegisters.java | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterHandler.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterHandler.java index afcaa81d38ce9..5b1d55f793465 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterHandler.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterHandler.java @@ -107,7 +107,7 @@ private List buildRequests() { } } - if (currentRequest.size() > 0) { + if (!currentRequest.isEmpty()) { requests.add(new ModbusRequest(currentRequest, getSlaveId())); } logger.debug("Created {} modbus request templates.", requests.size()); diff --git a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java index 00d5720e3cf6f..12517febfb7a5 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java +++ b/bundles/org.openhab.binding.modbus.sungrow/src/main/java/org/openhab/binding/modbus/sungrow/internal/SungrowInverterRegisters.java @@ -187,21 +187,36 @@ public enum SungrowInverterRegisters { /** * 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 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; @@ -209,6 +224,8 @@ public int getRegisterCount() { /** * Returns the channel group. + * + * @return channel group id. */ public String getChannelGroup() { return channelGroup; @@ -216,6 +233,8 @@ public String getChannelGroup() { /** * Returns the channel name. + * + * @return the channel name. */ public String getChannelName() { return this.name().toLowerCase().replace('_', '-'); @@ -223,6 +242,9 @@ public String getChannelName() { /** * 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); From a52835c187253317e3f0b5b4243683d6bb4ccb1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20K=C3=BCper?= Date: Sat, 17 Feb 2024 19:15:53 +0100 Subject: [PATCH 29/30] Applied some review remarks in documentation. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sönke Küper --- .../org.openhab.binding.modbus.sungrow/README.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/bundles/org.openhab.binding.modbus.sungrow/README.md b/bundles/org.openhab.binding.modbus.sungrow/README.md index fa85cdce9ab68..85daf05a1d3e3 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/README.md +++ b/bundles/org.openhab.binding.modbus.sungrow/README.md @@ -1,8 +1,7 @@ # 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. +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 @@ -29,7 +28,7 @@ As defined within the spec mentioned above the following inverters are supported The binding supports only one thing: -- `sungrowInverter`: The sungrow inverter +- `sungrow-inverter`: The sungrow inverter ## Preparation @@ -49,7 +48,7 @@ Enabling modbus and whitelist setting can be done in WiNet-S Web-UI as shown bel 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. -### `sungrowInverter` Thing Configuration +### `sungrow-inverter` Thing Configuration | Name | Type | Description | Default | Required | Advanced | |--------------|---------|--------------------------------------|---------|----------|----------| @@ -57,8 +56,7 @@ You just have to select the configured bridge and optional configure the polling ## Channels -The `sungrowInverter` thing has channels that serve the current state of the sungrow inverter, -as you are used to from the iSolareCloud Website and App. +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 | |------------------------------------|--------------------------|---------------------------------------|-----------|---------------------| @@ -109,7 +107,7 @@ as you are used to from the iSolareCloud Website and App. This example shows how to configure a sungrow inverter connected via modbus and uses the most common channels. -sungrow.things +_sungrow.things_ ```java Bridge modbus:tcp:sungrowBridge [ host="10.0.0.2", port=502, id=1, enableDiscovery=false ] { @@ -117,7 +115,7 @@ Bridge modbus:tcp:sungrowBridge [ host="10.0.0.2", port=502, id=1, enableDiscove } ``` -sungrow.items +_sungrow.items_ ```java // Groups From 3e087b90ed73bd1d0bdee6e11091cc8439cbebe3 Mon Sep 17 00:00:00 2001 From: Leo Siepel Date: Mon, 19 Feb 2024 14:33:57 +0100 Subject: [PATCH 30/30] Add sitemap example Signed-off-by: Leo Siepel --- .../README.md | 37 ++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/bundles/org.openhab.binding.modbus.sungrow/README.md b/bundles/org.openhab.binding.modbus.sungrow/README.md index 85daf05a1d3e3..79a87f2cf34b6 100644 --- a/bundles/org.openhab.binding.modbus.sungrow/README.md +++ b/bundles/org.openhab.binding.modbus.sungrow/README.md @@ -3,7 +3,7 @@ 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 +## Supported Inverters As defined within the spec mentioned above the following inverters are supported, but not all are tested yet: @@ -46,9 +46,9 @@ Enabling modbus and whitelist setting can be done in WiNet-S Web-UI as shown bel ## 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. +You just have to select the configured bridge and optional configure the polling interval. -### `sungrow-inverter` Thing Configuration +### Sungrow Inverter (`sungrow-inverter`) | Name | Type | Description | Default | Required | Advanced | |--------------|---------|--------------------------------------|---------|----------|----------| @@ -103,7 +103,7 @@ The `sungrow-inverter` thing has channels that serve the current state of the su | 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 | -## Configuration Example +## Full Example This example shows how to configure a sungrow inverter connected via modbus and uses the most common channels. @@ -144,4 +144,31 @@ Number:Energy daily_import_energy "Daily Import Energy" (gridInformation) ["Meas // 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"}``` +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 + } +} +```