Skip to content

[Edge] Implement Inepro Pro380ModCT meter#3570

Open
tomflorentin wants to merge 7 commits intoOpenEMS:developfrom
VEV-platform-services:feat/meter-inepro-380
Open

[Edge] Implement Inepro Pro380ModCT meter#3570
tomflorentin wants to merge 7 commits intoOpenEMS:developfrom
VEV-platform-services:feat/meter-inepro-380

Conversation

@tomflorentin
Copy link

@tomflorentin tomflorentin commented Feb 18, 2026

Dear OpenEMS team,

I am from VEV. We recently forked OpenEMS, and I’m proud to share our first open-source contribution.

We are beginners with OpenEMS, so please feel free to share any feedback or suggestions.

Inepro Pro380 ModCT Energy Meter

We purchased and installed this three-phase meter for our first proof of concept.
As part of this project, we developed and successfully tested its integration with OpenEMS.

Inepro Pro380 ModCT Energy Meter

Modbus table:
https://7549cdd3-cc68-4b56-9f7c-0db19b0a4c60.filesusr.com/ugd/511b90_fd5e7961835344e9addd80a2badafc97.pdf


Thanks all for your amazing work !

Looking forward to seeing you in Karlsruhe.

@tomflorentin tomflorentin marked this pull request as ready for review February 18, 2026 16:14
@tomflorentin tomflorentin changed the title [Meter] Implement Inepro Pro380ModCT meter with configuration and Mod… [Meter] Implement Inepro Pro380ModCT meter Feb 18, 2026
Copy link
Contributor

@sfeilmeier sfeilmeier left a comment

Choose a reason for hiding this comment

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

Marking this PR as "waiting for changes"

@Sn0w3y Sn0w3y changed the title [Meter] Implement Inepro Pro380ModCT meter [Edge] Implement Inepro Pro380ModCT meter Feb 25, 2026
public interface Pro380modct extends ElectricityMeter, OpenemsComponent {

public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
APPARENT_POWER_L1(Doc.of(OpenemsType.INTEGER).unit(Unit.VOLT_AMPERE)), //
Copy link
Collaborator

Choose a reason for hiding this comment

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

package io.openems.edge.meter.inepro.pro380modct;

import io.openems.common.channel.Unit;
import io.openems.common.types.OpenemsType;
import io.openems.edge.common.channel.Doc;
import io.openems.edge.common.component.OpenemsComponent;
import io.openems.edge.meter.api.ElectricityMeter;

public interface Pro380modct extends ElectricityMeter, OpenemsComponent {

	public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
		APPARENT_POWER_L1(Doc.of(OpenemsType.INTEGER)//
				.unit(Unit.VOLT_AMPERE)), //
		APPARENT_POWER_L2(Doc.of(OpenemsType.INTEGER)//
				.unit(Unit.VOLT_AMPERE)), //
		APPARENT_POWER_L3(Doc.of(OpenemsType.INTEGER)//
				.unit(Unit.VOLT_AMPERE)), //
		APPARENT_POWER(Doc.of(OpenemsType.INTEGER)//
				.unit(Unit.VOLT_AMPERE));

		private final Doc doc;

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

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

}


String webconsole_configurationFactory_nameHint() default "io.openems.edge.meter.pro380modct [{id}]";

@AttributeDefinition(name = "Meter-Type", description = "What is measured by this Meter?")
Copy link
Collaborator

Choose a reason for hiding this comment

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

package io.openems.edge.meter.inepro.pro380modct;

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

import io.openems.common.types.MeterType;

@ObjectClassDefinition(//
		name = "Meter inepro Pro380ModCT", //
		description = "Implements the inepro Pro380ModCT meter.")
public @interface Config {

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

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

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

	@AttributeDefinition(name = "Modbus-ID", description = "ID of Modbus bridge.")
	String modbus_id() default "modbus0";

	@AttributeDefinition(name = "Modbus Unit-ID", description = "The Unit-ID of the Modbus device.")
	int modbusUnitId() default 1;

	@AttributeDefinition(name = "Meter-Type", description = "What is measured by this Meter?")
	MeterType type() default MeterType.CONSUMPTION_METERED;

	@AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.")
	String Modbus_target() default "(enabled=true)";

	String webconsole_configurationFactory_nameHint() default "io.openems.edge.meter.pro380modct [{id}]";
}

Copy link
Collaborator

@Sn0w3y Sn0w3y left a comment

Choose a reason for hiding this comment

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

Please apply Formatting in Eclipse aswell (Ctrl+Shift+F)

Also maybe add the PDF to a "doc" Folder

Also see other comments

@tomflorentin
Copy link
Author

Thanks for all your comments, it helps me to learn faster. I did some code changes (locally) and will test them tomorrow on the meter, i will push the changes after.

@tomflorentin
Copy link
Author

I made the changes, and tested on our meter, it works fine. All your comments were very good.
I hope we are now ready to go :)

@tomflorentin tomflorentin requested a review from Sn0w3y February 26, 2026 16:00
@codecov
Copy link

codecov bot commented Feb 26, 2026

Codecov Report

❌ Patch coverage is 92.24806% with 10 lines in your changes missing coverage. Please review.

Additional details and impacted files
@@              Coverage Diff              @@
##             develop    #3570      +/-   ##
=============================================
- Coverage      59.41%   58.63%   -0.77%     
  Complexity       105      105              
=============================================
  Files           3069     3093      +24     
  Lines         133076   134134    +1058     
  Branches        9815     9884      +69     
=============================================
- Hits           79049    78642     -407     
- Misses         51045    52592    +1547     
+ Partials        2982     2900      -82     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.



public class Pro380modctImpl extends AbstractOpenemsModbusComponent
implements Pro380modct, ElectricityMeter, ModbusComponent, OpenemsComponent {
Copy link
Collaborator

Choose a reason for hiding this comment

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

ModbusSlave is missing - It would still be inherited from the Interface, but Best Practice would list it here

import io.openems.common.exceptions.OpenemsException;
import io.openems.common.types.MeterType;
import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent;
import io.openems.edge.bridge.modbus.api.BridgeModbus;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Also please apply Ctrl+Shift+O after all for Sorting the imports

private ConfigurationAdmin cm;

@Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY)
protected void setModbus(BridgeModbus modbus) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Method overrides AbstractOpenemsModbusComponent.setModbus(). The @OverRide annotation provides compile-time safety and documents intent.

}

private Config config = null;
private boolean invert;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Creates a second source of truth. KDK accesses this.config.invert() directly. Since this.config is assigned before super.activate() calls defineModbusProtocol(), direct access is safe.

@ObjectClassDefinition(//
name = "Meter inepro Pro380ModCT", //
description = "Implements the inepro Pro380ModCT meter.")
public @interface Config {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Config should be package-private - it's an internal implementation detail, not part of the public api

@Sn0w3y
Copy link
Collaborator

Sn0w3y commented Feb 27, 2026

In general very good First Implementation! But please use https://github.com/OpenEMS/openems/blob/develop/io.openems.edge.meter.kdk/src/io/openems/edge/meter/kdk/puct2/MeterKdk2puctImpl.java as a Reference and check, if this implementation of the kdk2puct also works with your meter as the Register Addresses seem to be 1:1 identical? So there would not be a need to Implement this Meter as a seperate Component i guess @sfeilmeier ? @tomflorentin

@Sn0w3y Sn0w3y self-requested a review February 27, 2026 09:57
@tomflorentin
Copy link
Author

It looks like the Kdk2 implementation works with this meter.
I wasn't aware of that compatibility. Lets close this PR for now.

@sfeilmeier
Copy link
Contributor

@tomflorentin Thank you for the effort!
@Sn0w3y Thank you for reviewing and identifying the similarity to KDK. It seems there are more identical meters in the market, like "Solar-Log Pro380-CT"

Instead of just closing this PR, we should make sure this information can be found in future. We could either:

  • Add a note to the README - but I don't know if anybody will read it
  • Create a full new Component (like in this PR), but by reusing code with KDK

@sfeilmeier sfeilmeier reopened this Mar 1, 2026
@Sn0w3y
Copy link
Collaborator

Sn0w3y commented Mar 1, 2026

I would reuse the Code 1:1 for this Meter aswell - maybe also add the SolarLog Meter if feasible in this PR aswell?

@sfeilmeier
Copy link
Contributor

Ok - so which path should we go? Full new Components for Inepro and Solar-Log and use abstract class that holds the modbus mappings? I am not sure tomflorentin can do that just yet. And it's a company repository so he cannot open the PR for write-access.

@Sn0w3y
Copy link
Collaborator

Sn0w3y commented Mar 1, 2026

Ok - so which path should we go? Full new Components for Inepro and Solar-Log and use abstract class that holds the modbus mappings? I am not sure tomflorentin can do that just yet. And it's a company repository so he cannot open the PR for write-access.

If you want maybe you could open a new PR and we can work on it together ( @tomflorentin and me) ? :)

@tomflorentin
Copy link
Author

Yes @Sn0w3y , i would be happy to help.

Could you email me a better way to communicate and keep in touch please ?

tom.florentin@vev.com

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants