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

[modbus.sbc] Initial contribution #9174

Merged
merged 13 commits into from
Feb 2, 2021
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions bom/openhab-addons/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,11 @@
<artifactId>org.openhab.binding.modbus.helioseasycontrols</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.modbus.sbc</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.modbus.stiebeleltron</artifactId>
Expand Down
13 changes: 13 additions & 0 deletions bundles/org.openhab.binding.modbus.sbc/NOTICE
Original file line number Diff line number Diff line change
@@ -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
74 changes: 74 additions & 0 deletions bundles/org.openhab.binding.modbus.sbc/README.md
Original file line number Diff line number Diff line change
@@ -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"
}
```
26 changes: 26 additions & 0 deletions bundles/org.openhab.binding.modbus.sbc/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.1.0-SNAPSHOT</version>
</parent>

<artifactId>org.openhab.binding.modbus.sbc</artifactId>

<name>openHAB Add-ons :: Bundles :: Modbus SBC Binding</name>

<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.modbus</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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<ModbusReadRequestBlueprint> 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());
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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");
}
Loading