How to use "ctrlIoChannelSingleThreshold0"?

We would like to switch a consumer on/off depending on the electricity cost. We are using a Shelly Pro 3 as contractor for the three phases, the ctrlIoChannelSingleThreshold as controller and the timeOfUseTariff component for the price.
The controller is configured as depicted in the screenshot:

But the controller seems not to work, the debug log shows:

8.1.2026, 18:34:55
WARN
io.openems.edge.core.cycle.CycleWorker
[_cycle] Error in Controller [ctrlIoChannelSingleThreshold0]. ClassCastException: class io.openems.edge.common.channel.DoubleReadChannel cannot be cast to class io.openems.edge.common.channel.IntegerReadChannel (io.openems.edge.common.channel.DoubleReadChannel and io.openems.edge.common.channel.IntegerReadChannel are in unnamed module of loader org.apache.felix.framework.BundleWiringImpl$BundleClassLoader @3316dc5f)

We also tried to specify the Threshold as 300 instead of 300.0, leading to the same result.

We additionally noted, that the channel _sum/GridBuyPrice uses €/MWh and the log uses €/kWh – we assumed, that the channel value is more relevant, thus using the threshold value of 300 (as €/MWh).

Can someone please give a hint what’s going wrong? Thx for any help!
Simon

PS: The meaning of the Mode (default: automatic) within the controller isn’t clear to us. Can someone explain it to us?

The problem is not the entered threshold value, but the type of the input channel.

_sum/GridBuyPrice is of type DoubleReadChannel

but the input channel of a ControllerIoChannelSingleThreshold has to be of type IntegerReadChannel:

IntegerReadChannel inputChannel = this.componentManager.getChannel(inputChannelAddress);

Maybe the variable inputChannel should be changed to DoubleReadChannel and there should be a handler that determines the input channel type and converts the value to Double.

1 Like

Thx for your reply!

I created a PR (I believe an explicit cast is not needed, as integer, long, float get implicitly casted to double):

Still looking for some insights for this: :slight_smile:

I don’t think this is enough. The threshold is still int and the comparison is done with integers:

		int inputValue;
		if (inputValueOpt.isPresent()) {
			inputValue = (int) Math.round(inputValueOpt.getAsDouble());
...
			if (inputValue <= this.config.threshold())
...
			if (inputValue > this.config.threshold()) {
...

The switch can be manually set in the GUI https://docs.fenecon.de/en/fems/fems-app/App_Schwellwertsteuerung.html

	public void run() throws OpenemsNamedException {
		var outputChannels = this.getOutputChannels();

		switch (this.config.mode()) {
		case ON:
			this.setOutputs(outputChannels, true);
			break;
		case OFF:
			this.setOutputs(outputChannels, false);
			break;
		case AUTOMATIC:
			this.automaticMode(outputChannels);
			break;
		}
	}

1 Like

This was the piece of information I was missing.
What would be the use case to specify something different than automatic during setting up the component? There I did find the drop-down (without any further documentation) and was a little confused.

I created a PR to add a link to the documentation to the readme: Update readme.adoc: added link to documentation by sjjh Ā· Pull Request #3511 Ā· OpenEMS/openems Ā· GitHub

The comparison already included the (int) Math.round(inputValueOpt.getAsDouble()), in my understanding already accepting a Double and intentionally casting it to an int. Thus I thought it would be fine to keep it. Do you believe it would be better to not round() the value and compare as Double? I’m not understanding the domain/business logic enough to make an informed decision, thus needing some feedback. I’m happy to alter my PR. :slight_smile:

In my eyes comparing Doubles as Ints doesn’t make sense, even if the loss of precision seems not much. If someone wants a very precise threshold, the system should not tell him ā€œof course you can enter this value, but unfortunately the comparison doesn’t consider it completelyā€œ.

If comparing power, energy or prices it may be neglect-able, but maybe there are some other cases where this would be an unwanted limitation.

The component seems originally to be built for power, so there should be done more to adapt it to any unit, at least in the front end, where ā€œWā€ seems to be hard coded at some places.

Fair enough, I’ll try to improve the PR.

Noticed that and some other UI issues as well. Will create an additional ticket for these.

Edit: Updated my PR 3510. @s_h I would be happy if you can have a look.

These are exactly the same changes I did in my version, but without testing.

Sorry, I didn’t see your version.
I did test my changes locally with my use case and it runs without any exceptions.

It seems that the CI build in the PR fails, but I don’t really understand why.

Oh no, this was a misunderstanding. I’ve made these changes in my local version, just to see if the compiler has any complaints. There is no pull request ore something else.

1 Like

I’ve made a PR for the UI:

https://github.com/OpenEMS/openems/pull/3515

1 Like