diff --git a/CODEOWNERS b/CODEOWNERS index ace489c4bc9..1f67b3c3411 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -158,6 +158,7 @@ /bundles/org.openhab.binding.modbus/ @ssalonen /bundles/org.openhab.binding.modbus.e3dc/ @weymann /bundles/org.openhab.binding.modbus.helioseasycontrols/ @bern77 +/bundles/org.openhab.binding.modbus.sbc/ @fwolter /bundles/org.openhab.binding.modbus.stiebeleltron/ @pail23 /bundles/org.openhab.binding.modbus.studer/ @giovannimirulla /bundles/org.openhab.binding.modbus.sunspec/ @mrbig diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index 78f2ff07eb1..8569f1c6e39 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -776,6 +776,11 @@ org.openhab.binding.modbus.helioseasycontrols ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.modbus.sbc + ${project.version} + org.openhab.addons.bundles org.openhab.binding.modbus.stiebeleltron diff --git a/bundles/org.openhab.binding.modbus.sbc/NOTICE b/bundles/org.openhab.binding.modbus.sbc/NOTICE new file mode 100644 index 00000000000..38d625e3492 --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sbc/NOTICE @@ -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 diff --git a/bundles/org.openhab.binding.modbus.sbc/README.md b/bundles/org.openhab.binding.modbus.sbc/README.md new file mode 100644 index 00000000000..d1d75f35353 --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sbc/README.md @@ -0,0 +1,74 @@ +# Modbus Saia Burgess Controls Binding + +This binding interfaces the energy meter series ALD1 by Saia Burgess Controls (SBC) via Modbus. + +## Supported Things + +The following Things are supported: + +- `ald1Unidirectional`: 1-phase 32A one-way energy meter ALD1D5FD00A3A00 +- `ald1Bidirectional`: 1-phase 32A two-way energy meter ALD1B5FD00A3A00 + +## Discovery + +This binding does not support discovery. + +## Thing Configuration + +The following configuration parameter applys to `ald1Unidirectional` and `ald1Bidirectional`. + +| Name | Description | Type | Required | +|---------------|------------------------------------------|---------|----------| +| pollInterval | Time between polling the data in ms | Integer | yes | + +The Thing needs a Modbus serial slave Bridge to operate. + +One of the following serial settings need to be configured in the Bridge: + +- 9600 baud, 2 stop bit, no parity +- 9600 baud, 1 stop bit, even parity +- 9600 baud, 1 stop bit, odd parity + +## Channels + +The following Channels apply to `ald1Unidirectional` and `ald1Bidirectional` if not stated otherwise. + +| Name | Type | Description | +|---------------------|--------------------------|---------------------------------------------------------------| +| total_energy | Number:Energy | Energy Total | +| partial_energy | Number:Energy | Energy Counter Resettable (only unidirectional meter) | +| feeding_back_energy | Number:Energy | Energy Feeding Back (only bidirectional meter) | +| voltage | Number:ElectricPotential | Effective Voltage | +| current | Number:ElectricCurrent | Effective Current | +| active_power | Number:Power | Effective Active Power (negative numbers mean feeding back) | +| reactive_power | Number:Power | Effective Reactive Power (negative numbers mean feeding back) | +| power_factor | Number:Dimensionless | Power Factor | + +## Full Example + +### .items + +``` +Number:Energy ALD1_Total_Energy "[%.2f %unit%]" {channel="modbus:ald1Bidirectional:8b6e85623b:total_energy"} +Number:Energy ALD1_Feeding_Back_Energy "[%.2f %unit%]" {channel="modbus:ald1Bidirectional:8b6e85623b:feeding_back_energy"} +Number:ElectricPotential ALD1_Voltage "[%d %unit%]" {channel="modbus:ald1Bidirectional:8b6e85623b:voltage"} +Number:ElectricCurrent ALD1_Current "[%.1f %unit%]" {channel="modbus:ald1Bidirectional:8b6e85623b:current"} +Number:Power ALD1_Active_Power "[%.2f %unit%]" {channel="modbus:ald1Bidirectional:8b6e85623b:active_power"} +Number:Power ALD1_Reactive_Power "[%.2f %unit%]" {channel="modbus:ald1Bidirectional:8b6e85623b:reactive_power"} +Number:Dimensionless ALD1_Power_Factor "[%.2f]" {channel="modbus:ald1Bidirectional:8b6e85623b:power_factor"} +``` + +### .sitemap + +``` +sitemap ald1 label="ALD1 Energy Meter" +{ + Default item=ALD1_Total_Energy label="Total Energy" + Default item=ALD1_Feeding_Back_Energy label="Feeding Back Energy" + Default item=ALD1_Voltage label="Voltage" + Default item=ALD1_Current label="Current" + Default item=ALD1_Active_Power label="Active Power" + Default item=ALD1_Reactive_Power label="Reactive Power" + Default item=ALD1_Power_Factor label="Power Factor" +} +``` diff --git a/bundles/org.openhab.binding.modbus.sbc/pom.xml b/bundles/org.openhab.binding.modbus.sbc/pom.xml new file mode 100644 index 00000000000..7da99d2601a --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sbc/pom.xml @@ -0,0 +1,26 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 3.1.0-SNAPSHOT + + + org.openhab.binding.modbus.sbc + + openHAB Add-ons :: Bundles :: Modbus SBC Binding + + + + org.openhab.addons.bundles + org.openhab.binding.modbus + ${project.version} + provided + + + + diff --git a/bundles/org.openhab.binding.modbus.sbc/src/main/java/org/openhab/binding/modbus/sbc/internal/ALD1Configuration.java b/bundles/org.openhab.binding.modbus.sbc/src/main/java/org/openhab/binding/modbus/sbc/internal/ALD1Configuration.java new file mode 100644 index 00000000000..d69e491e3b1 --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sbc/src/main/java/org/openhab/binding/modbus/sbc/internal/ALD1Configuration.java @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2010-2021 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.modbus.sbc.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link ALD1Configuration} class contains fields mapping thing configuration parameters. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class ALD1Configuration { + public int pollInterval; +} diff --git a/bundles/org.openhab.binding.modbus.sbc/src/main/java/org/openhab/binding/modbus/sbc/internal/ALD1Handler.java b/bundles/org.openhab.binding.modbus.sbc/src/main/java/org/openhab/binding/modbus/sbc/internal/ALD1Handler.java new file mode 100644 index 00000000000..6952cf35efc --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sbc/src/main/java/org/openhab/binding/modbus/sbc/internal/ALD1Handler.java @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2010-2021 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.modbus.sbc.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.modbus.handler.BaseModbusThingHandler; +import org.openhab.core.io.transport.modbus.AsyncModbusFailure; +import org.openhab.core.io.transport.modbus.AsyncModbusReadResult; +import org.openhab.core.io.transport.modbus.ModbusBitUtilities; +import org.openhab.core.io.transport.modbus.ModbusReadFunctionCode; +import org.openhab.core.io.transport.modbus.ModbusReadRequestBlueprint; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.types.Command; +import org.openhab.core.types.RefreshType; + +/** + * The {@link ALD1Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class ALD1Handler extends BaseModbusThingHandler { + private static final int FIRST_READ_REGISTER = 28; + private static final int READ_LENGTH = 13; + private static final int TRIES = 1; + private ALD1Configuration config = new ALD1Configuration(); + private @Nullable ModbusReadRequestBlueprint blueprint; + + public ALD1Handler(Thing thing) { + super(thing); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + ModbusReadRequestBlueprint localBlueprint = blueprint; + if (command instanceof RefreshType && localBlueprint != null) { + submitOneTimePoll(localBlueprint, this::readSuccessful, this::readError); + } + } + + @Override + public void modbusInitialize() { + config = getConfigAs(ALD1Configuration.class); + + if (config.pollInterval <= 0) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "Invalid poll interval: " + config.pollInterval); + return; + } + + ModbusReadRequestBlueprint localBlueprint = blueprint = new ModbusReadRequestBlueprint(getSlaveId(), + ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS, FIRST_READ_REGISTER - 1, READ_LENGTH, TRIES); + + updateStatus(ThingStatus.UNKNOWN); + + registerRegularPoll(localBlueprint, config.pollInterval, 0, this::readSuccessful, this::readError); + } + + private void readSuccessful(AsyncModbusReadResult result) { + result.getRegisters().ifPresent(registers -> { + if (getThing().getStatus() != ThingStatus.ONLINE) { + updateStatus(ThingStatus.ONLINE); + } + + for (ALD1Registers channel : ALD1Registers.values()) { + int index = channel.getRegisterNumber() - FIRST_READ_REGISTER; + + ModbusBitUtilities.extractStateFromRegisters(registers, index, channel.getType()) + .map(d -> d.toBigDecimal().multiply(channel.getMultiplier())) + .map(bigDecimal -> new QuantityType<>(bigDecimal, channel.getUnit())) + .ifPresent(v -> updateState(createChannelUid(channel), v)); + } + }); + } + + private void readError(AsyncModbusFailure error) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Failed to retrieve data: " + error.getCause().getMessage()); + } + + private ChannelUID createChannelUid(ALD1Registers channel) { + return new ChannelUID(thing.getUID(), channel.toString().toLowerCase()); + } +} diff --git a/bundles/org.openhab.binding.modbus.sbc/src/main/java/org/openhab/binding/modbus/sbc/internal/ALD1Registers.java b/bundles/org.openhab.binding.modbus.sbc/src/main/java/org/openhab/binding/modbus/sbc/internal/ALD1Registers.java new file mode 100644 index 00000000000..077b6eb4c38 --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sbc/src/main/java/org/openhab/binding/modbus/sbc/internal/ALD1Registers.java @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2010-2021 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.modbus.sbc.internal; + +import static org.openhab.core.io.transport.modbus.ModbusConstants.ValueType.*; + +import java.math.BigDecimal; + +import javax.measure.Unit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.io.transport.modbus.ModbusConstants; +import org.openhab.core.io.transport.modbus.ModbusConstants.ValueType; +import org.openhab.core.library.unit.Units; + +/** + * The {@link ALD1Registers} is responsible for defining Modbus registers and their units. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public enum ALD1Registers { + // the following register numbers are 1-based. They need to be converted before sending them on the wire. + TOTAL_ENERGY(0.01f, 28, UINT32, Units.KILOWATT_HOUR), + PARTIAL_ENERGY(0.01f, 30, UINT32, Units.KILOWATT_HOUR), // only unidirectional meters + FEEDING_BACK_ENERGY(0.01f, 30, UINT32, Units.KILOWATT_HOUR), // only bidirectional meters + VOLTAGE(1, 36, UINT16, Units.VOLT), + CURRENT(0.1f, 37, UINT16, Units.AMPERE), + ACTIVE_POWER(10, 38, INT16, Units.WATT), + REACTIVE_POWER(10, 39, INT16, Units.VAR), + POWER_FACTOR(0.01f, 40, INT16, Units.ONE); + + private BigDecimal multiplier; + private int registerNumber; + private ModbusConstants.ValueType type; + private Unit unit; + + private ALD1Registers(float multiplier, int registerNumber, ValueType type, Unit unit) { + this.multiplier = new BigDecimal(multiplier); + this.registerNumber = registerNumber; + this.type = type; + this.unit = unit; + } + + public Unit getUnit() { + return unit; + } + + public BigDecimal getMultiplier() { + return multiplier; + } + + public int getRegisterNumber() { + return registerNumber; + } + + public ModbusConstants.ValueType getType() { + return type; + } +} diff --git a/bundles/org.openhab.binding.modbus.sbc/src/main/java/org/openhab/binding/modbus/sbc/internal/SBCBindingConstants.java b/bundles/org.openhab.binding.modbus.sbc/src/main/java/org/openhab/binding/modbus/sbc/internal/SBCBindingConstants.java new file mode 100644 index 00000000000..8b00a41eff3 --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sbc/src/main/java/org/openhab/binding/modbus/sbc/internal/SBCBindingConstants.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2010-2021 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.modbus.sbc.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.modbus.ModbusBindingConstants; +import org.openhab.core.thing.ThingTypeUID; + +/** + * The {@link SBCBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class SBCBindingConstants { + public static final ThingTypeUID THING_TYPE_ALD1_UNIDIRECTIONAL = new ThingTypeUID( + ModbusBindingConstants.BINDING_ID, "ald1Unidirectional"); + public static final ThingTypeUID THING_TYPE_ALD1_BIDIRECTIONAL = new ThingTypeUID(ModbusBindingConstants.BINDING_ID, + "ald1Bidirectional"); +} diff --git a/bundles/org.openhab.binding.modbus.sbc/src/main/java/org/openhab/binding/modbus/sbc/internal/SBCHandlerFactory.java b/bundles/org.openhab.binding.modbus.sbc/src/main/java/org/openhab/binding/modbus/sbc/internal/SBCHandlerFactory.java new file mode 100644 index 00000000000..a7410861aa2 --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sbc/src/main/java/org/openhab/binding/modbus/sbc/internal/SBCHandlerFactory.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2010-2021 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.modbus.sbc.internal; + +import static org.openhab.binding.modbus.sbc.internal.SBCBindingConstants.*; + +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.binding.BaseThingHandlerFactory; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.annotations.Component; + +/** + * The {@link SBCHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +@Component(configurationPid = "binding.sbc", service = ThingHandlerFactory.class) +public class SBCHandlerFactory extends BaseThingHandlerFactory { + + private static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_ALD1_UNIDIRECTIONAL, + THING_TYPE_ALD1_BIDIRECTIONAL); + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + if (THING_TYPE_ALD1_UNIDIRECTIONAL.equals(thingTypeUID) || THING_TYPE_ALD1_BIDIRECTIONAL.equals(thingTypeUID)) { + return new ALD1Handler(thing); + } + + return null; + } +} diff --git a/bundles/org.openhab.binding.modbus.sbc/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.modbus.sbc/src/main/resources/OH-INF/config/config.xml new file mode 100644 index 00000000000..001cf9136bc --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sbc/src/main/resources/OH-INF/config/config.xml @@ -0,0 +1,15 @@ + + + + + + + Time between polling the data in ms. + 5000 + + + + diff --git a/bundles/org.openhab.binding.modbus.sbc/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.modbus.sbc/src/main/resources/OH-INF/thing/thing-types.xml new file mode 100644 index 00000000000..d62cd11cb7a --- /dev/null +++ b/bundles/org.openhab.binding.modbus.sbc/src/main/resources/OH-INF/thing/thing-types.xml @@ -0,0 +1,100 @@ + + + + + + + + + + + Unidirectional one-phase energy meter connected via Modbus. + + + + + + + + + + + + + + + + + + + + + + Bidirectional one-phase energy meter connected via Modbus. + + + + + + + + + + + + + + + + Number:Energy + + + + + + Number:Energy + + + + + + Number:Energy + + + + + + Number:ElectricPotential + + + + + + Number:ElectricCurrent + + + + + + Number:Power + + Negative numbers mean feeding back + + + + + Number:Power + + Negative numbers mean feeding back + + + + + Number:Dimensionless + + + + diff --git a/bundles/pom.xml b/bundles/pom.xml index 8915314b721..9722fb7f97c 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -188,6 +188,7 @@ org.openhab.binding.minecraft org.openhab.binding.modbus org.openhab.binding.modbus.e3dc + org.openhab.binding.modbus.sbc org.openhab.binding.modbus.studer org.openhab.binding.modbus.sunspec org.openhab.binding.modbus.stiebeleltron diff --git a/features/openhab-addons/src/main/resources/footer.xml b/features/openhab-addons/src/main/resources/footer.xml index 291d0536502..d38fbdf3ff2 100644 --- a/features/openhab-addons/src/main/resources/footer.xml +++ b/features/openhab-addons/src/main/resources/footer.xml @@ -31,6 +31,7 @@ mvn:org.openhab.addons.bundles/org.openhab.binding.modbus/${project.version} mvn:org.openhab.addons.bundles/org.openhab.binding.modbus.e3dc/${project.version} mvn:org.openhab.addons.bundles/org.openhab.binding.modbus.helioseasycontrols/${project.version} + mvn:org.openhab.addons.bundles/org.openhab.binding.modbus.sbc/${project.version} mvn:org.openhab.addons.bundles/org.openhab.binding.modbus.stiebeleltron/${project.version} mvn:org.openhab.addons.bundles/org.openhab.binding.modbus.studer/${project.version} mvn:org.openhab.addons.bundles/org.openhab.binding.modbus.sunspec/${project.version}