From 77ca9f14b90655221d9884328060bd200b57d833 Mon Sep 17 00:00:00 2001 From: sebastian asen Date: Thu, 3 Mar 2022 13:25:22 +0100 Subject: [PATCH] Add Fronius PV Inverter (via Modbus/SunSpec) (#207) - Implement Fronius PV Inverter bundle - AbstractOpenemsSunSpecComponent readNextBlock() function change to complete when all expected blocks have already been read. - Add Factory ID in isProducer() method to be displayed in the UI. Co-authored-by: Sebastian Asen Co-authored-by: Stefan Feilmeier Reviewed-on: https://git.intranet.fenecon.de/FENECON/fems/pulls/207 Reviewed-by: Stefan Feilmeier Co-authored-by: sebastian asen Co-committed-by: sebastian asen --- io.openems.edge.application/EdgeApp.bndrun | 2 + .../AbstractOpenemsSunSpecComponent.java | 33 +++++- io.openems.edge.pvinverter.fronius/.classpath | 12 ++ io.openems.edge.pvinverter.fronius/.gitignore | 2 + io.openems.edge.pvinverter.fronius/.project | 23 ++++ io.openems.edge.pvinverter.fronius/bnd.bnd | 17 +++ ..._Float_v1.0_with_SYMOHYBRID_MODEL_124.xlsx | Bin 0 -> 52160 bytes .../readme.adoc | 12 ++ .../edge/pvinverter/fronius/Config.java | 30 +++++ .../pvinverter/fronius/FroniusPvInverter.java | 110 ++++++++++++++++++ .../test/.gitignore | 0 .../fronius/FroniusPvInverterTest.java | 26 +++++ .../edge/pvinverter/fronius/MyConfig.java | 68 +++++++++++ ui/src/app/shared/edge/edgeconfig.ts | 58 ++++----- 14 files changed, 351 insertions(+), 42 deletions(-) create mode 100644 io.openems.edge.pvinverter.fronius/.classpath create mode 100644 io.openems.edge.pvinverter.fronius/.gitignore create mode 100644 io.openems.edge.pvinverter.fronius/.project create mode 100644 io.openems.edge.pvinverter.fronius/bnd.bnd create mode 100644 io.openems.edge.pvinverter.fronius/doc/Inverter_Register_Map_Float_v1.0_with_SYMOHYBRID_MODEL_124.xlsx create mode 100644 io.openems.edge.pvinverter.fronius/readme.adoc create mode 100644 io.openems.edge.pvinverter.fronius/src/io/openems/edge/pvinverter/fronius/Config.java create mode 100644 io.openems.edge.pvinverter.fronius/src/io/openems/edge/pvinverter/fronius/FroniusPvInverter.java create mode 100644 io.openems.edge.pvinverter.fronius/test/.gitignore create mode 100644 io.openems.edge.pvinverter.fronius/test/io/openems/edge/pvinverter/fronius/FroniusPvInverterTest.java create mode 100644 io.openems.edge.pvinverter.fronius/test/io/openems/edge/pvinverter/fronius/MyConfig.java diff --git a/io.openems.edge.application/EdgeApp.bndrun b/io.openems.edge.application/EdgeApp.bndrun index 59f45b18cfc..33a163f7320 100644 --- a/io.openems.edge.application/EdgeApp.bndrun +++ b/io.openems.edge.application/EdgeApp.bndrun @@ -134,6 +134,7 @@ bnd.identity;id='io.openems.edge.predictor.persistencemodel',\ bnd.identity;id='io.openems.edge.predictor.similardaymodel',\ bnd.identity;id='io.openems.edge.pvinverter.cluster',\ + bnd.identity;id='io.openems.edge.pvinverter.fronius',\ bnd.identity;id='io.openems.edge.pvinverter.kaco.blueplanet',\ bnd.identity;id='io.openems.edge.pvinverter.sma',\ bnd.identity;id='io.openems.edge.pvinverter.solarlog',\ @@ -275,6 +276,7 @@ io.openems.edge.predictor.similardaymodel;version=snapshot,\ io.openems.edge.pvinverter.api;version=snapshot,\ io.openems.edge.pvinverter.cluster;version=snapshot,\ + io.openems.edge.pvinverter.fronius;version=snapshot,\ io.openems.edge.pvinverter.kaco.blueplanet;version=snapshot,\ io.openems.edge.pvinverter.sma;version=snapshot,\ io.openems.edge.pvinverter.solarlog;version=snapshot,\ diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/sunspec/AbstractOpenemsSunSpecComponent.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/sunspec/AbstractOpenemsSunSpecComponent.java index 84cf0576812..4ac18cada20 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/sunspec/AbstractOpenemsSunSpecComponent.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/sunspec/AbstractOpenemsSunSpecComponent.java @@ -5,6 +5,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Optional; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -77,6 +78,10 @@ protected boolean activate(ComponentContext context, String id, String alias, bo throws OpenemsException { this.readFromCommonBlockNo = readFromCommonBlockNo; + var expectedBlocks = this.activeModels.keySet().stream() // + .map(m -> m.getBlockId()) // + .collect(Collectors.toSet()); + // Start the SunSpec read procedure... this.isSunSpec().thenAccept(isSunSpec -> { if (!isSunSpec) { @@ -84,7 +89,7 @@ protected boolean activate(ComponentContext context, String id, String alias, bo } try { - this.readNextBlock(40_002).thenRun(() -> { + this.readNextBlock(40_002, expectedBlocks).thenRun(() -> { this.isSunSpecInitializationCompleted = true; this.onSunSpecInitializationCompleted(); }); @@ -129,12 +134,28 @@ private CompletableFuture isSunSpec() throws OpenemsException { /** * Reads the next SunSpec block. * - * @param startAddress the startAddress + * @param startAddress the startAddress + * @param remainingBlocks the remaining blocks expected to read * @return a future that completes once reading the block finished * @throws OpenemsException on error */ - private CompletableFuture readNextBlock(int startAddress) throws OpenemsException { + private CompletableFuture readNextBlock(int startAddress, Set remainingBlocks) + throws OpenemsException { final CompletableFuture finished = new CompletableFuture(); + + // Finish if all expected Blocks have been read + if (remainingBlocks.isEmpty()) { + finished.complete(null); + } + + /* + * Try to read block by block until all required blocks have been read or an + * END_OF_MAP register has been found. + * + * It may still happen that a device does not have a valid END_OF_MAP register + * and that some blocks are not read - especially when one component is used for + * multiple devices like single and three phase inverter. + */ this.readElementsOnceTyped(new UnsignedWordElement(startAddress), new UnsignedWordElement(startAddress + 1)) .thenAccept(values -> { int blockId = values.get(0); @@ -164,6 +185,7 @@ private CompletableFuture readNextBlock(int startAddress) throws OpenemsEx Priority priority = activeEntry.getValue(); try { this.addBlock(startAddress, sunSpecModel, priority); + remainingBlocks.remove(activeEntry.getKey().getBlockId()); } catch (OpenemsException e) { this.logWarn(this.log, "Error while adding SunSpec-Model [" + blockId + "] starting at [" + startAddress + "]: " + e.getMessage()); @@ -180,7 +202,9 @@ private CompletableFuture readNextBlock(int startAddress) throws OpenemsEx // Read next block recursively int nextBlockStartAddress = startAddress + 2 + length; try { - final CompletableFuture readNextBlockFuture = this.readNextBlock(nextBlockStartAddress); + + final CompletableFuture readNextBlockFuture = this.readNextBlock(nextBlockStartAddress, + remainingBlocks); // Announce finished when next block (recursively) is finished readNextBlockFuture.thenRun(() -> { finished.complete(null); @@ -250,7 +274,6 @@ public boolean isSunSpecInitializationCompleted() { * @param startAddress the address to start reading from * @param model the SunSpecModel * @param priority the reading priority - * @return future that gets completed when the Block elements are read * @throws OpenemsException on error */ protected void addBlock(int startAddress, SunSpecModel model, Priority priority) throws OpenemsException { diff --git a/io.openems.edge.pvinverter.fronius/.classpath b/io.openems.edge.pvinverter.fronius/.classpath new file mode 100644 index 00000000000..43d68895d03 --- /dev/null +++ b/io.openems.edge.pvinverter.fronius/.classpath @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/io.openems.edge.pvinverter.fronius/.gitignore b/io.openems.edge.pvinverter.fronius/.gitignore new file mode 100644 index 00000000000..36573722b7e --- /dev/null +++ b/io.openems.edge.pvinverter.fronius/.gitignore @@ -0,0 +1,2 @@ +/generated/ +/bin_test/ diff --git a/io.openems.edge.pvinverter.fronius/.project b/io.openems.edge.pvinverter.fronius/.project new file mode 100644 index 00000000000..bb6537fa409 --- /dev/null +++ b/io.openems.edge.pvinverter.fronius/.project @@ -0,0 +1,23 @@ + + + io.openems.edge.pvinverter.fronius + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/io.openems.edge.pvinverter.fronius/bnd.bnd b/io.openems.edge.pvinverter.fronius/bnd.bnd new file mode 100644 index 00000000000..cc7e461359a --- /dev/null +++ b/io.openems.edge.pvinverter.fronius/bnd.bnd @@ -0,0 +1,17 @@ +Bundle-Name: OpenEMS Edge PV Inverter Fronius +Bundle-Vendor: FENECON GmbH +Bundle-License: https://opensource.org/licenses/EPL-2.0 +Bundle-Version: 1.0.0.${tstamp} + +-buildpath: \ + ${buildpath},\ + io.openems.common,\ + io.openems.edge.bridge.modbus,\ + io.openems.edge.common,\ + io.openems.edge.meter.api,\ + io.openems.edge.pvinverter.api,\ + io.openems.edge.pvinverter.sunspec + +-testpath: \ + ${testpath},\ + com.ghgande.j2mod \ No newline at end of file diff --git a/io.openems.edge.pvinverter.fronius/doc/Inverter_Register_Map_Float_v1.0_with_SYMOHYBRID_MODEL_124.xlsx b/io.openems.edge.pvinverter.fronius/doc/Inverter_Register_Map_Float_v1.0_with_SYMOHYBRID_MODEL_124.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..2cd78d969bf44134e0eb6f5fbb7bc96930a74c30 GIT binary patch literal 52160 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 =ipJ1339Q`PwYF3HdsbBok27GwW#GjhLcZ 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 literal 0 HcmV?d00001 diff --git a/io.openems.edge.pvinverter.fronius/readme.adoc b/io.openems.edge.pvinverter.fronius/readme.adoc new file mode 100644 index 00000000000..4476fb9ac55 --- /dev/null +++ b/io.openems.edge.pvinverter.fronius/readme.adoc @@ -0,0 +1,12 @@ += Fronius PV inverter + +Implementation of the Fronius PV inverters. + +Tested on +- [Fronius Symo] + +Implemented Natures: +- SymmetricMeter +- ManagedSymmetricPvInverter + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.pvinverter.fronius[Source Code icon:github[]] \ No newline at end of file diff --git a/io.openems.edge.pvinverter.fronius/src/io/openems/edge/pvinverter/fronius/Config.java b/io.openems.edge.pvinverter.fronius/src/io/openems/edge/pvinverter/fronius/Config.java new file mode 100644 index 00000000000..302a1496689 --- /dev/null +++ b/io.openems.edge.pvinverter.fronius/src/io/openems/edge/pvinverter/fronius/Config.java @@ -0,0 +1,30 @@ +package io.openems.edge.pvinverter.fronius; + +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; + +@ObjectClassDefinition(name = "PV-Inverter Fronius", // + description = "Implements the Fronius PV inverter.") +@interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") + String id() default "pvInverter0"; + + @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 = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.") + String Modbus_target() default "(enabled=true)"; + + String webconsole_configurationFactory_nameHint() default "PV-Inverter Fronius [{id}]"; + +} diff --git a/io.openems.edge.pvinverter.fronius/src/io/openems/edge/pvinverter/fronius/FroniusPvInverter.java b/io.openems.edge.pvinverter.fronius/src/io/openems/edge/pvinverter/fronius/FroniusPvInverter.java new file mode 100644 index 00000000000..c89380d9dbe --- /dev/null +++ b/io.openems.edge.pvinverter.fronius/src/io/openems/edge/pvinverter/fronius/FroniusPvInverter.java @@ -0,0 +1,110 @@ +package io.openems.edge.pvinverter.fronius; + +import java.util.Map; + +import org.osgi.service.cm.ConfigurationAdmin; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.osgi.service.component.annotations.ReferencePolicyOption; +import org.osgi.service.event.Event; +import org.osgi.service.event.EventConstants; +import org.osgi.service.event.EventHandler; +import org.osgi.service.metatype.annotations.Designate; + +import com.google.common.collect.ImmutableMap; + +import io.openems.common.channel.AccessMode; +import io.openems.common.exceptions.OpenemsException; +import io.openems.edge.bridge.modbus.api.BridgeModbus; +import io.openems.edge.bridge.modbus.api.ModbusComponent; +import io.openems.edge.bridge.modbus.sunspec.DefaultSunSpecModel; +import io.openems.edge.bridge.modbus.sunspec.SunSpecModel; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.common.event.EdgeEventConstants; +import io.openems.edge.common.modbusslave.ModbusSlave; +import io.openems.edge.common.modbusslave.ModbusSlaveNatureTable; +import io.openems.edge.common.modbusslave.ModbusSlaveTable; +import io.openems.edge.common.taskmanager.Priority; +import io.openems.edge.meter.api.SymmetricMeter; +import io.openems.edge.pvinverter.api.ManagedSymmetricPvInverter; +import io.openems.edge.pvinverter.sunspec.AbstractSunSpecPvInverter; +import io.openems.edge.pvinverter.sunspec.SunSpecPvInverter; + +@Designate(ocd = Config.class, factory = true) +@Component(// + name = "PV-Inverter.Fronius", // + immediate = true, // + configurationPolicy = ConfigurationPolicy.REQUIRE, // + property = { // + EventConstants.EVENT_TOPIC + "=" + EdgeEventConstants.TOPIC_CYCLE_EXECUTE_WRITE, // + "type=PRODUCTION" // + }) +public class FroniusPvInverter extends AbstractSunSpecPvInverter implements SunSpecPvInverter, + ManagedSymmetricPvInverter, SymmetricMeter, ModbusComponent, OpenemsComponent, EventHandler, ModbusSlave { + + private static final Map ACTIVE_MODELS = ImmutableMap.builder() + .put(DefaultSunSpecModel.S_1, Priority.LOW) // from 40002 + + /* + * This is depending on the specific inverter. + */ + .put(DefaultSunSpecModel.S_111, Priority.LOW) // from 40070 + .put(DefaultSunSpecModel.S_112, Priority.LOW) // from 40070 + .put(DefaultSunSpecModel.S_113, Priority.HIGH) // from 40070 + .build(); + + private static final int READ_FROM_MODBUS_BLOCK = 1; + + @Reference + protected ConfigurationAdmin cm; + + public FroniusPvInverter() throws OpenemsException { + super(// + ACTIVE_MODELS, // + OpenemsComponent.ChannelId.values(), // + ModbusComponent.ChannelId.values(), // + SymmetricMeter.ChannelId.values(), // + ManagedSymmetricPvInverter.ChannelId.values(), // + SunSpecPvInverter.ChannelId.values() // + ); + } + + @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) + protected void setModbus(BridgeModbus modbus) { + super.setModbus(modbus); + } + + @Activate + void activate(ComponentContext context, Config config) throws OpenemsException { + if (super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, + "Modbus", config.modbus_id(), READ_FROM_MODBUS_BLOCK)) { + return; + } + } + + @Deactivate + protected void deactivate() { + super.deactivate(); + } + + @Override + public void handleEvent(Event event) { + super.handleEvent(event); + } + + @Override + public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) { + return new ModbusSlaveTable(// + OpenemsComponent.getModbusSlaveNatureTable(accessMode), // + SymmetricMeter.getModbusSlaveNatureTable(accessMode), // + ManagedSymmetricPvInverter.getModbusSlaveNatureTable(accessMode), // + ModbusSlaveNatureTable.of(FroniusPvInverter.class, accessMode, 100) // + .build()); + } +} diff --git a/io.openems.edge.pvinverter.fronius/test/.gitignore b/io.openems.edge.pvinverter.fronius/test/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/io.openems.edge.pvinverter.fronius/test/io/openems/edge/pvinverter/fronius/FroniusPvInverterTest.java b/io.openems.edge.pvinverter.fronius/test/io/openems/edge/pvinverter/fronius/FroniusPvInverterTest.java new file mode 100644 index 00000000000..dbd11d5cb1b --- /dev/null +++ b/io.openems.edge.pvinverter.fronius/test/io/openems/edge/pvinverter/fronius/FroniusPvInverterTest.java @@ -0,0 +1,26 @@ +package io.openems.edge.pvinverter.fronius; + +import org.junit.Test; + +import io.openems.edge.bridge.modbus.test.DummyModbusBridge; +import io.openems.edge.common.test.ComponentTest; +import io.openems.edge.common.test.DummyConfigurationAdmin; + +public class FroniusPvInverterTest { + + private static final String PV_INVERTER_ID = "pvInverter0"; + private static final String MODBUS_ID = "modbus0"; + + @Test + public void test() throws Exception { + new ComponentTest(new FroniusPvInverter()) // + .addReference("cm", new DummyConfigurationAdmin()) // + .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) // + .activate(MyConfig.create() // + .setId(PV_INVERTER_ID) // + .setModbusId(MODBUS_ID) // + .setModbusUnitId(1) // + .build()) // + ; + } +} \ No newline at end of file diff --git a/io.openems.edge.pvinverter.fronius/test/io/openems/edge/pvinverter/fronius/MyConfig.java b/io.openems.edge.pvinverter.fronius/test/io/openems/edge/pvinverter/fronius/MyConfig.java new file mode 100644 index 00000000000..636c183ca49 --- /dev/null +++ b/io.openems.edge.pvinverter.fronius/test/io/openems/edge/pvinverter/fronius/MyConfig.java @@ -0,0 +1,68 @@ +package io.openems.edge.pvinverter.fronius; + +import io.openems.common.utils.ConfigUtils; +import io.openems.edge.common.test.AbstractComponentConfig; + +@SuppressWarnings("all") +public class MyConfig extends AbstractComponentConfig implements Config { + + protected static class Builder { + private String id = null; + private String modbusId = null; + private int modbusUnitId; + + private Builder() { + } + + public Builder setId(String id) { + this.id = id; + return this; + } + + public Builder setModbusId(String modbusId) { + this.modbusId = modbusId; + return this; + } + + public Builder setModbusUnitId(int modbusUnitId) { + this.modbusUnitId = modbusUnitId; + return this; + } + + public MyConfig build() { + return new MyConfig(this); + } + } + + /** + * Create a Config builder. + * + * @return a {@link Builder} + */ + public static Builder create() { + return new Builder(); + } + + private final Builder builder; + + private MyConfig(Builder builder) { + super(Config.class, builder.id); + this.builder = builder; + } + + @Override + public String modbus_id() { + return this.builder.modbusId; + } + + @Override + public String Modbus_target() { + return ConfigUtils.generateReferenceTargetFilter(this.id(), this.modbus_id()); + } + + @Override + public int modbusUnitId() { + return this.builder.modbusUnitId; + } + +} \ No newline at end of file diff --git a/ui/src/app/shared/edge/edgeconfig.ts b/ui/src/app/shared/edge/edgeconfig.ts index bd658efec27..6e79da91a74 100644 --- a/ui/src/app/shared/edge/edgeconfig.ts +++ b/ui/src/app/shared/edge/edgeconfig.ts @@ -1,3 +1,4 @@ +import { is } from 'date-fns/locale'; import { ChannelAddress } from '../type/channeladdress'; import { AdvertWidgets, Widgets } from '../type/widget'; import { Edge } from './edge'; @@ -270,26 +271,8 @@ export class EdgeConfig { // Do we have a Meter with type PRODUCTION? for (let component of this.getComponentsImplementingNature("io.openems.edge.meter.api.SymmetricMeter")) { if (component.isEnabled) { - // TODO make sure 'type' is provided for all Meters - if (component.properties['type'] == "PRODUCTION") { - return true; - } - // TODO remove, once all Edges are at least version 2019.15 - switch (component.factoryId) { - case 'Fenecon.Mini.PvMeter': - case 'Fenecon.Dess.PvMeter': - case 'Fenecon.Pro.PvMeter': - case 'Kostal.Piko.Charger': - case 'Kaco.BlueplanetHybrid10.PvInverter': - case 'PV-Inverter.Solarlog': - case 'PV-Inverter.KACO.blueplanet': - case 'PV-Inverter.SunSpec': - case 'PV-Inverter.SMA.SunnyTripower': - case 'SolarEdge.PV-Inverter': - case 'Simulator.PvInverter': - case 'Simulator.ProductionMeter.Acting': - return true; - } + + return this.isProducer(component); } } return false; @@ -304,24 +287,25 @@ export class EdgeConfig { public isProducer(component: EdgeConfig.Component) { if (component.properties['type'] == "PRODUCTION") { return true; - } else { - // TODO properties in OSGi Component annotations are not transmitted correctly with Apache Felix SCR - switch (component.factoryId) { - case 'Fenecon.Mini.PvMeter': - case 'Fenecon.Dess.PvMeter': - case 'Fenecon.Pro.PvMeter': - case 'Kostal.Piko.Charger': - case 'Kaco.BlueplanetHybrid10.PvInverter': - case 'PV-Inverter.Solarlog': - case 'PV-Inverter.KACO.blueplanet': - case 'PV-Inverter.SunSpec': - case 'PV-Inverter.SMA.SunnyTripower': - case 'SolarEdge.PV-Inverter': - case 'Simulator.PvInverter': - case 'Simulator.ProductionMeter.Acting': - return true; - } } + // TODO properties in OSGi Component annotations are not transmitted correctly with Apache Felix SCR + switch (component.factoryId) { + case 'Fenecon.Dess.PvMeter': + case 'Fenecon.Mini.PvMeter': + case 'Fenecon.Pro.PvMeter': + case 'Kaco.BlueplanetHybrid10.PvInverter': + case 'Kostal.Piko.Charger': + case 'PV-Inverter.Fronius': + case 'PV-Inverter.KACO.blueplanet': + case 'PV-Inverter.SMA.SunnyTripower': + case 'PV-Inverter.Solarlog': + case 'PV-Inverter.SunSpec': + case 'Simulator.ProductionMeter.Acting': + case 'Simulator.PvInverter': + case 'SolarEdge.PV-Inverter': + return true; + } + return false; }