Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implements openDTU (for Hoymiles Micro Inverter) #2566

Open
wants to merge 17 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions io.openems.edge.application/EdgeApp.bndrun
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@
bnd.identity;id='io.openems.edge.io.gpio',\
bnd.identity;id='io.openems.edge.io.kmtronic',\
bnd.identity;id='io.openems.edge.io.offgridswitch',\
bnd.identity;id='io.openems.edge.io.opendtu',\
bnd.identity;id='io.openems.edge.io.revpi',\
bnd.identity;id='io.openems.edge.io.shelly',\
bnd.identity;id='io.openems.edge.io.wago',\
Expand Down Expand Up @@ -303,6 +304,7 @@
io.openems.edge.io.gpio;version=snapshot,\
io.openems.edge.io.kmtronic;version=snapshot,\
io.openems.edge.io.offgridswitch;version=snapshot,\
io.openems.edge.io.opendtu;version=snapshot,\
io.openems.edge.io.revpi;version=snapshot,\
io.openems.edge.io.shelly;version=snapshot,\
io.openems.edge.io.wago;version=snapshot,\
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ public void subscribeTime(TimeEndpoint endpoint) {

@Override
public CompletableFuture<String> request(Endpoint endpoint) {
// TODO Auto-generated method stub
return null;
// Always return a CompletableFuture completed with an empty JSON object
return CompletableFuture.completedFuture("{}");
}

}
12 changes: 12 additions & 0 deletions io.openems.edge.io.opendtu/.classpath
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="aQute.bnd.classpath.container"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
<classpathentry kind="src" output="bin" path="src"/>
<classpathentry kind="src" output="bin_test" path="test">
<attributes>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="bin"/>
</classpath>
2 changes: 2 additions & 0 deletions io.openems.edge.io.opendtu/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/bin_test/
/generated/
23 changes: 23 additions & 0 deletions io.openems.edge.io.opendtu/.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>io.openems.edge.io.opendtu</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>bndtools.core.bndbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>bndtools.core.bndnature</nature>
</natures>
</projectDescription>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
eclipse.preferences.version=1
encoding/<project>=UTF-8
16 changes: 16 additions & 0 deletions io.openems.edge.io.opendtu/bnd.bnd
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Bundle-Name: OpenEMS Edge IO OpenDTU
Bundle-Vendor: Haller Johannes & Thomas Klinkenberg
Bundle-License: https://opensource.org/licenses/EPL-2.0
Bundle-Version: 1.0.0.${tstamp}

-buildpath: \
${buildpath},\
io.openems.common,\
io.openems.edge.bridge.http,\
io.openems.edge.common,\
io.openems.edge.io.api,\
io.openems.edge.meter.api,\
io.openems.edge.pvinverter.api,\
io.openems.edge.timedata.api
-testpath: \
${testpath}
8 changes: 8 additions & 0 deletions io.openems.edge.io.opendtu/readme.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
= openDTU Hoymiles

This bundle implements openDTU for Hoymiles Inverters.

Compatible with
-https://github.com/tbnobody/OpenDTU

https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.io.opendtu[Source Code icon:github[]]
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package io.openems.edge.io.opendtu.inverter;

import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.AttributeType;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;

import io.openems.edge.meter.api.MeterType;
import io.openems.edge.meter.api.SinglePhase;

@ObjectClassDefinition(//
name = "openDTU Hoymiles Inverter", //
description = "Implements the openDTU for Hoymiles Inverter.")
@interface Config {

@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
String id() default "io0";

@AttributeDefinition(name = "Alias", description = "Human-readable name of this Component; defaults to Component-ID")
String alias() default "";

@AttributeDefinition(name = "Username", description = "Username for openDTU to make settings possible")
String username() default "";

@AttributeDefinition(name = "Password", description = "Password for oprnDTU to make settings possible", type = AttributeType.PASSWORD)
String password() default "";

@AttributeDefinition(name = "Is enabled?", description = "Is this Component enabled?")
boolean enabled() default true;

@AttributeDefinition(name = "Phase", description = "Which Phase is this Inverter connected to?")
SinglePhase phase() default SinglePhase.L1;

@AttributeDefinition(name = "IP-Address", description = "The IP address of the openDTU.")
String ip();

@AttributeDefinition(name = "Inverter Serial Number", description = "The serial number of the inverter connected to the DTU")
String serialNumber() default "";

@AttributeDefinition(name = "Meter-Type", description = "What is measured by this DTU?")
MeterType type() default MeterType.PRODUCTION;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is really only a pv inverter, like i would expect - you do not have to add the meter type in the config as it is always PRODUCTION.
Just set a fixed configuration property like this:
https://github.com/Sn0w3y/openems/blob/26cebce21d45f8f78edc93ed93e2084eb27c2750/io.openems.edge.pvinverter.fronius/src/io/openems/edge/pvinverter/fronius/PvInverterFroniusImpl.java#L44

Maybe you have to take care in the UI separately - i remember a method in the UI Part "isProduction".
Im not completely sure if there is still the need to do something in there.


@AttributeDefinition(name = "Initial Power Limit", description = "The initial power limit setting")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the "power limit" used for?

int initialPowerLimit() default 100;

String webconsole_configurationFactory_nameHint() default "IO openDTU [{id}]";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package io.openems.edge.io.opendtu.inverter;

import org.osgi.service.event.EventHandler;

import io.openems.common.channel.AccessMode;
import io.openems.common.channel.Level;
import io.openems.common.channel.PersistencePriority;
import io.openems.common.channel.Unit;
import io.openems.common.types.OpenemsType;
import io.openems.edge.common.channel.Doc;
import io.openems.edge.common.channel.StateChannel;
import io.openems.edge.common.channel.WriteChannel;
import io.openems.edge.common.channel.value.Value;
import io.openems.edge.common.component.OpenemsComponent;
import io.openems.edge.meter.api.ElectricityMeter;
import io.openems.edge.meter.api.SinglePhaseMeter;

public interface Opendtu extends SinglePhaseMeter, ElectricityMeter, OpenemsComponent, EventHandler {

/**
* Channel for setting the Power Limit.
*
* @return bla ToDo change return
*/
public default WriteChannel<Integer> setPowerLimit() {
return this.channel(ChannelId.POWER_LIMIT);
}

public static enum ChannelId implements io.openems.edge.common.channel.ChannelId {
/**
* Slave Communication Failed Fault.
*
* <ul>
* <li>Interface: Opendtu
* <li>Type: State
* </ul>
*/
SLAVE_COMMUNICATION_FAILED(Doc.of(Level.FAULT)), //

/**
* Maximum Ever Actual Power.
*
* <ul>
* <li>Interface: Ess DC Charger
* <li>Type: Integer
* <li>Unit: W
* <li>Range: positive or '0'
* <li>Implementation Note: value is automatically derived from ACTUAL_POWER
* </ul>
*/
MAX_ACTUAL_POWER(Doc.of(OpenemsType.INTEGER)//
.unit(Unit.WATT) //
.persistencePriority(PersistencePriority.HIGH)), //

/**
* Limit Status.
*
* <ul>
* <li>Type: String
* <li>Unit: Not applicable
* <li>Range: "Ok", "Pending" or "Failure"
* </ul>
*/
LIMIT_STATUS(Doc.of(OpenemsType.STRING)//
.text("Limit Status")),

/**
* The Relative Limit of the max. Power.
*
* <ul>
* <li>Type: Integer
* <li>Unit: Percent
* <li>Range: 0 to 100
* <li>Access Mode: Read/Write
* <li>Implementation Note: Adjusts the maximum allowable power output relative
* to the inverters max. Power.
* </ul>
*/
POWER_LIMIT(Doc.of(OpenemsType.INTEGER)//
.unit(Unit.PERCENT) //
.text("The Relative Limit of the max. Power.").accessMode(AccessMode.READ_WRITE)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

//


/**
* Power Limit Fault.
*
* <ul>
* <li>Interface: Opendtu
* <li>Type: State
* </ul>
*/
POWER_LIMIT_FAULT(Doc.of(Level.FAULT)); //

private final Doc doc;

private ChannelId(Doc doc) {
this.doc = doc;
}

@Override
public Doc doc() {
return this.doc;
}
}

/**
* Gets the Channel for {@link ChannelId#SLAVE_COMMUNICATION_FAILED}.
*
* @return the Channel
*/
public default StateChannel getSlaveCommunicationFailedChannel() {
return this.channel(ChannelId.SLAVE_COMMUNICATION_FAILED);
}

/**
* Gets the Slave Communication Failed State. See
* {@link ChannelId#SLAVE_COMMUNICATION_FAILED}.
*
* @return the Channel {@link Value}
*/
public default Value<Boolean> getSlaveCommunicationFailed() {
return this.getSlaveCommunicationFailedChannel().value();
}

/**
* Internal method to set the 'nextValue' on
* {@link ChannelId#SLAVE_COMMUNICATION_FAILED} Channel.
*
* @param value the next value
*/
public default void _setSlaveCommunicationFailed(boolean value) {
this.getSlaveCommunicationFailedChannel().setNextValue(value);
}
}
Loading