Skip to content

Commit

Permalink
[growatt] Binding for Growatt solar inverters (openhab#15120)
Browse files Browse the repository at this point in the history
* [growatt] initial contribution

Signed-off-by: Andrew Fiddian-Green <[email protected]>
Signed-off-by: Andras Uhrin <[email protected]>
  • Loading branch information
andrewfg authored and andrasU committed Jan 27, 2024
1 parent 2ee23e5 commit 60da9a0
Show file tree
Hide file tree
Showing 35 changed files with 4,331 additions and 1 deletion.
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@
/bundles/org.openhab.binding.gree/ @markus7017
/bundles/org.openhab.binding.groheondus/ @FlorianSW
/bundles/org.openhab.binding.groupepsa/ @arjanmels
/bundles/org.openhab.binding.growatt/ @andrewfg
/bundles/org.openhab.binding.guntamatic/ @MikeTheTux
/bundles/org.openhab.binding.haassohnpelletstove/ @chingon007
/bundles/org.openhab.binding.harmonyhub/ @digitaldan
Expand Down
5 changes: 5 additions & 0 deletions bom/openhab-addons/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,11 @@
<artifactId>org.openhab.binding.groupepsa</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.growatt</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.guntamatic</artifactId>
Expand Down
13 changes: 13 additions & 0 deletions bundles/org.openhab.binding.growatt/NOTICE
Original file line number Diff line number Diff line change
@@ -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
349 changes: 349 additions & 0 deletions bundles/org.openhab.binding.growatt/README.md

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions bundles/org.openhab.binding.growatt/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>4.2.0-SNAPSHOT</version>
</parent>

<artifactId>org.openhab.binding.growatt</artifactId>

<name>openHAB Add-ons :: Bundles :: Growatt Binding</name>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.growatt-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>

<feature name="openhab-binding-growatt" description="Growatt Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.growatt/${project.version}</bundle>
</feature>
</features>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Copyright (c) 2010-2024 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.growatt.internal;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;

/**
* The {@link GrowattBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Andrew Fiddian-Green - Initial contribution
*/
@NonNullByDefault
public class GrowattBindingConstants {

public static final String BINDING_ID = "growatt";

public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "bridge");
public static final ThingTypeUID THING_TYPE_INVERTER = new ThingTypeUID(BINDING_ID, "inverter");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
/**
* Copyright (c) 2010-2024 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.growatt.internal;

import java.util.AbstractMap;
import java.util.Map;

import javax.measure.Unit;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.Units;

/**
* The {@link GrowattChannels} class defines the channel ids and respective UoM and scaling factors.
*
* @author Andrew Fiddian-Green - Initial contribution
*/
@NonNullByDefault
public class GrowattChannels {

/**
* Class encapsulating units of measure and scale information.
*/
public static class UoM {
public final Unit<?> units;
public final float divisor;

public UoM(Unit<?> units, float divisor) {
this.units = units;
this.divisor = divisor;
}
}

/**
* Map of the channel ids to their respective UoM and scaling factors
*/
private static final Map<String, UoM> CHANNEL_ID_UOM_MAP = Map.ofEntries(
// inverter state
new AbstractMap.SimpleEntry<String, UoM>("system-status", new UoM(Units.ONE, 1)),

// solar generation
new AbstractMap.SimpleEntry<String, UoM>("pv-power", new UoM(Units.WATT, 10)),

// electric data for strings #1 and #2
new AbstractMap.SimpleEntry<String, UoM>("pv1-voltage", new UoM(Units.VOLT, 10)),
new AbstractMap.SimpleEntry<String, UoM>("pv1-current", new UoM(Units.AMPERE, 10)),
new AbstractMap.SimpleEntry<String, UoM>("pv1-power", new UoM(Units.WATT, 10)),

new AbstractMap.SimpleEntry<String, UoM>("pv2-voltage", new UoM(Units.VOLT, 10)),
new AbstractMap.SimpleEntry<String, UoM>("pv2-current", new UoM(Units.AMPERE, 10)),
new AbstractMap.SimpleEntry<String, UoM>("pv2-power", new UoM(Units.WATT, 10)),

// grid electric data (1-phase resp. 3-phase)
new AbstractMap.SimpleEntry<String, UoM>("grid-frequency", new UoM(Units.HERTZ, 100)),

new AbstractMap.SimpleEntry<String, UoM>("grid-voltage-r", new UoM(Units.VOLT, 10)),
new AbstractMap.SimpleEntry<String, UoM>("grid-voltage-s", new UoM(Units.VOLT, 10)),
new AbstractMap.SimpleEntry<String, UoM>("grid-voltage-t", new UoM(Units.VOLT, 10)),
new AbstractMap.SimpleEntry<String, UoM>("grid-voltage-rs", new UoM(Units.VOLT, 10)),
new AbstractMap.SimpleEntry<String, UoM>("grid-voltage-st", new UoM(Units.VOLT, 10)),
new AbstractMap.SimpleEntry<String, UoM>("grid-voltage-tr", new UoM(Units.VOLT, 10)),

// inverter output
new AbstractMap.SimpleEntry<String, UoM>("inverter-current-r", new UoM(Units.AMPERE, 10)),
new AbstractMap.SimpleEntry<String, UoM>("inverter-current-s", new UoM(Units.AMPERE, 10)),
new AbstractMap.SimpleEntry<String, UoM>("inverter-current-t", new UoM(Units.AMPERE, 10)),

new AbstractMap.SimpleEntry<String, UoM>("inverter-power", new UoM(Units.WATT, 10)),
new AbstractMap.SimpleEntry<String, UoM>("inverter-power-r", new UoM(Units.WATT, 10)),
new AbstractMap.SimpleEntry<String, UoM>("inverter-power-s", new UoM(Units.WATT, 10)),
new AbstractMap.SimpleEntry<String, UoM>("inverter-power-t", new UoM(Units.WATT, 10)),

new AbstractMap.SimpleEntry<String, UoM>("inverter-va", new UoM(Units.VOLT_AMPERE, 10)),

// battery discharge / charge power
new AbstractMap.SimpleEntry<String, UoM>("charge-current", new UoM(Units.AMPERE, 10)),
new AbstractMap.SimpleEntry<String, UoM>("charge-power", new UoM(Units.WATT, 10)),

new AbstractMap.SimpleEntry<String, UoM>("discharge-power", new UoM(Units.WATT, 10)),
new AbstractMap.SimpleEntry<String, UoM>("discharge-va", new UoM(Units.VOLT_AMPERE, 10)),

// export power to grid
new AbstractMap.SimpleEntry<String, UoM>("export-power", new UoM(Units.WATT, 10)),
new AbstractMap.SimpleEntry<String, UoM>("export-power-r", new UoM(Units.WATT, 10)),
new AbstractMap.SimpleEntry<String, UoM>("export-power-s", new UoM(Units.WATT, 10)),
new AbstractMap.SimpleEntry<String, UoM>("export-power-t", new UoM(Units.WATT, 10)),

// power to user
new AbstractMap.SimpleEntry<String, UoM>("import-power", new UoM(Units.WATT, 10)),
new AbstractMap.SimpleEntry<String, UoM>("import-power-r", new UoM(Units.WATT, 10)),
new AbstractMap.SimpleEntry<String, UoM>("import-power-s", new UoM(Units.WATT, 10)),
new AbstractMap.SimpleEntry<String, UoM>("import-power-t", new UoM(Units.WATT, 10)),

// power to local
new AbstractMap.SimpleEntry<String, UoM>("load-power", new UoM(Units.WATT, 10)),
new AbstractMap.SimpleEntry<String, UoM>("load-power-r", new UoM(Units.WATT, 10)),
new AbstractMap.SimpleEntry<String, UoM>("load-power-s", new UoM(Units.WATT, 10)),
new AbstractMap.SimpleEntry<String, UoM>("load-power-t", new UoM(Units.WATT, 10)),

// inverter output energy
new AbstractMap.SimpleEntry<String, UoM>("inverter-energy-today", new UoM(Units.KILOWATT_HOUR, 10)),
new AbstractMap.SimpleEntry<String, UoM>("inverter-energy-total", new UoM(Units.KILOWATT_HOUR, 10)),

// solar DC input energy
new AbstractMap.SimpleEntry<String, UoM>("pv-energy-today", new UoM(Units.KILOWATT_HOUR, 10)),
new AbstractMap.SimpleEntry<String, UoM>("pv1-energy-today", new UoM(Units.KILOWATT_HOUR, 10)),
new AbstractMap.SimpleEntry<String, UoM>("pv2-energy-today", new UoM(Units.KILOWATT_HOUR, 10)),

new AbstractMap.SimpleEntry<String, UoM>("pv-energy-total", new UoM(Units.KILOWATT_HOUR, 10)),
new AbstractMap.SimpleEntry<String, UoM>("pv1-energy-total", new UoM(Units.KILOWATT_HOUR, 10)),
new AbstractMap.SimpleEntry<String, UoM>("pv2-energy-total", new UoM(Units.KILOWATT_HOUR, 10)),

// energy exported to grid
new AbstractMap.SimpleEntry<String, UoM>("export-energy-today", new UoM(Units.KILOWATT_HOUR, 10)),
new AbstractMap.SimpleEntry<String, UoM>("export-energy-total", new UoM(Units.KILOWATT_HOUR, 10)),

// energy imported from grid
new AbstractMap.SimpleEntry<String, UoM>("import-energy-today", new UoM(Units.KILOWATT_HOUR, 10)),
new AbstractMap.SimpleEntry<String, UoM>("import-energy-total", new UoM(Units.KILOWATT_HOUR, 10)),

// energy supplied to load
new AbstractMap.SimpleEntry<String, UoM>("load-energy-today", new UoM(Units.KILOWATT_HOUR, 10)),
new AbstractMap.SimpleEntry<String, UoM>("load-energy-total", new UoM(Units.KILOWATT_HOUR, 10)),

// energy imported to charge
new AbstractMap.SimpleEntry<String, UoM>("import-charge-energy-today", new UoM(Units.KILOWATT_HOUR, 10)),
new AbstractMap.SimpleEntry<String, UoM>("import-charge-energy-total", new UoM(Units.KILOWATT_HOUR, 10)),

// inverter energy to charge
new AbstractMap.SimpleEntry<String, UoM>("inverter-charge-energy-today", new UoM(Units.KILOWATT_HOUR, 10)),
new AbstractMap.SimpleEntry<String, UoM>("inverter-charge-energy-total", new UoM(Units.KILOWATT_HOUR, 10)),

// energy supplied from discharge
new AbstractMap.SimpleEntry<String, UoM>("discharge-energy-today", new UoM(Units.KILOWATT_HOUR, 10)),
new AbstractMap.SimpleEntry<String, UoM>("discharge-energy-total", new UoM(Units.KILOWATT_HOUR, 10)),

// inverter up time
new AbstractMap.SimpleEntry<String, UoM>("total-work-time", new UoM(Units.HOUR, 7200)),

// bus voltages
new AbstractMap.SimpleEntry<String, UoM>("p-bus-voltage", new UoM(Units.VOLT, 10)),
new AbstractMap.SimpleEntry<String, UoM>("n-bus-voltage", new UoM(Units.VOLT, 10)),
new AbstractMap.SimpleEntry<String, UoM>("sp-bus-voltage", new UoM(Units.VOLT, 10)),

// temperatures
new AbstractMap.SimpleEntry<String, UoM>("pv-temperature", new UoM(SIUnits.CELSIUS, 10)),
new AbstractMap.SimpleEntry<String, UoM>("pv-ipm-temperature", new UoM(SIUnits.CELSIUS, 10)),
new AbstractMap.SimpleEntry<String, UoM>("pv-boost-temperature", new UoM(SIUnits.CELSIUS, 10)),
new AbstractMap.SimpleEntry<String, UoM>("temperature-4", new UoM(SIUnits.CELSIUS, 10)),
new AbstractMap.SimpleEntry<String, UoM>("pv2-temperature", new UoM(SIUnits.CELSIUS, 10)),

// battery data
new AbstractMap.SimpleEntry<String, UoM>("battery-type", new UoM(Units.ONE, 1)),
new AbstractMap.SimpleEntry<String, UoM>("battery-voltage", new UoM(Units.VOLT, 10)),
new AbstractMap.SimpleEntry<String, UoM>("battery-temperature", new UoM(SIUnits.CELSIUS, 10)),
new AbstractMap.SimpleEntry<String, UoM>("battery-display", new UoM(Units.ONE, 10)),
new AbstractMap.SimpleEntry<String, UoM>("battery-soc", new UoM(Units.PERCENT, 1)),

// fault codes
new AbstractMap.SimpleEntry<String, UoM>("system-fault-0", new UoM(Units.ONE, 1)),
new AbstractMap.SimpleEntry<String, UoM>("system-fault-1", new UoM(Units.ONE, 1)),
new AbstractMap.SimpleEntry<String, UoM>("system-fault-2", new UoM(Units.ONE, 1)),
new AbstractMap.SimpleEntry<String, UoM>("system-fault-3", new UoM(Units.ONE, 1)),
new AbstractMap.SimpleEntry<String, UoM>("system-fault-4", new UoM(Units.ONE, 1)),
new AbstractMap.SimpleEntry<String, UoM>("system-fault-5", new UoM(Units.ONE, 1)),
new AbstractMap.SimpleEntry<String, UoM>("system-fault-6", new UoM(Units.ONE, 1)),
new AbstractMap.SimpleEntry<String, UoM>("system-fault-7", new UoM(Units.ONE, 1)),

// miscellaneous
new AbstractMap.SimpleEntry<String, UoM>("system-work-mode", new UoM(Units.ONE, 1)),
new AbstractMap.SimpleEntry<String, UoM>("sp-display-status", new UoM(Units.ONE, 10)),
new AbstractMap.SimpleEntry<String, UoM>("constant-power-ok", new UoM(Units.ONE, 1)),
new AbstractMap.SimpleEntry<String, UoM>("load-percent", new UoM(Units.PERCENT, 10)),

// reactive 'power' resp. 'energy'
new AbstractMap.SimpleEntry<String, UoM>("rac", new UoM(Units.VAR, 10)),
new AbstractMap.SimpleEntry<String, UoM>("erac-today", new UoM(Units.KILOVAR_HOUR, 10)),
new AbstractMap.SimpleEntry<String, UoM>("erac-total", new UoM(Units.KILOVAR_HOUR, 10))
//
);

public static Map<String, UoM> getMap() {
return GrowattChannels.CHANNEL_ID_UOM_MAP;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**
* Copyright (c) 2010-2024 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.growatt.internal.action;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.growatt.internal.handler.GrowattInverterHandler;
import org.openhab.core.automation.annotation.ActionInput;
import org.openhab.core.automation.annotation.RuleAction;
import org.openhab.core.thing.binding.ThingActions;
import org.openhab.core.thing.binding.ThingActionsScope;
import org.openhab.core.thing.binding.ThingHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Implementation of the {@link ThingActions} interface used for setting up battery charging and discharging programs.
*
* @author Andrew Fiddian-Green - Initial contribution
*/
@ThingActionsScope(name = "growatt")
@NonNullByDefault
public class GrowattActions implements ThingActions {

private final Logger logger = LoggerFactory.getLogger(GrowattActions.class);
private @Nullable GrowattInverterHandler handler;

public static void setupBatteryProgram(ThingActions actions, Integer programMode, @Nullable Integer powerLevel,
@Nullable Integer stopSOC, @Nullable Boolean enableAcCharging, @Nullable String startTime,
@Nullable String stopTime, @Nullable Boolean enableProgram) {
if (actions instanceof GrowattActions growattActions) {
growattActions.setupBatteryProgram(programMode, powerLevel, stopSOC, enableAcCharging, startTime, stopTime,
enableProgram);
} else {
throw new IllegalArgumentException("The 'actions' argument is not an instance of GrowattActions");
}
}

@Override
public @Nullable ThingHandler getThingHandler() {
return handler;
}

@Override
public void setThingHandler(@Nullable ThingHandler handler) {
this.handler = (handler instanceof GrowattInverterHandler growattHandler) ? growattHandler : null;
}

@RuleAction(label = "@text/actions.battery-program.label", description = "@text/actions.battery-program.description")
public void setupBatteryProgram(
@ActionInput(name = "program-mode", label = "@text/actions.program-mode.label", description = "@text/actions.program-mode.description") Integer programMode,
@ActionInput(name = "power-level", label = "@text/actions.power-level.label", description = "@text/actions.power-level.description") @Nullable Integer powerLevel,
@ActionInput(name = "stop-soc", label = "@text/actions.stop-soc.label", description = "@text/actions.stop-soc.description") @Nullable Integer stopSOC,
@ActionInput(name = "enable-ac-charging", label = "@text/actions.enable-ac-charging.label", description = "@text/actions.enable-ac-charging.description") @Nullable Boolean enableAcCharging,
@ActionInput(name = "start-time", label = "@text/actions.start-time.label", description = "@text/actions.start-time.description") @Nullable String startTime,
@ActionInput(name = "stop-time", label = "@text/actions.stop-time.label", description = "@text/actions.stop-time.description") @Nullable String stopTime,
@ActionInput(name = "enable-program", label = "@text/actions.enable-program.label", description = "@text/actions.enable-program.description") @Nullable Boolean enableProgram) {
GrowattInverterHandler handler = this.handler;
if (handler != null) {
handler.setupBatteryProgram(programMode, powerLevel, stopSOC, enableAcCharging, startTime, stopTime,
enableProgram);
} else {
logger.warn("ThingHandler is null.");
}
}
}
Loading

0 comments on commit 60da9a0

Please sign in to comment.