[modbus.sbc] Initial contribution (#9174)

Signed-off-by: Fabian Wolter <github@fabian-wolter.de>
This commit is contained in:
Fabian Wolter
2021-02-02 08:28:58 +01:00
committed by GitHub
parent ff3ff933af
commit 0be7f60438
14 changed files with 518 additions and 0 deletions

View File

@@ -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;
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}

View File

@@ -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");
}

View File

@@ -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<ThingTypeUID> 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;
}
}

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<config-description:config-descriptions
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
<config-description uri="thing-type:modbus-sbc:ald1">
<parameter name="pollInterval" type="integer" required="true" min="100" unit="ms">
<label>Poll Interval</label>
<description>Time between polling the data in ms.</description>
<default>5000</default>
</parameter>
</config-description>
</config-description:config-descriptions>

View File

@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="modbus"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="ald1Unidirectional">
<supported-bridge-type-refs>
<bridge-type-ref id="serial"/>
<bridge-type-ref id="tcp"/>
</supported-bridge-type-refs>
<label>ALD1 Energy Meter (Unid.)</label>
<description>Unidirectional one-phase energy meter connected via Modbus.</description>
<channels>
<channel id="total_energy" typeId="total_energy"/>
<channel id="partial_energy" typeId="partial_energy"/>
<channel id="voltage" typeId="voltage"/>
<channel id="current" typeId="current"/>
<channel id="active_power" typeId="active_power"/>
<channel id="reactive_power" typeId="reactive_power"/>
<channel id="power_factor" typeId="power_factor"/>
</channels>
<config-description-ref uri="thing-type:modbus-sbc:ald1"/>
</thing-type>
<thing-type id="ald1Bidirectional">
<supported-bridge-type-refs>
<bridge-type-ref id="serial"/>
<bridge-type-ref id="tcp"/>
</supported-bridge-type-refs>
<label>ALD1 Energy Meter (Bidi.)</label>
<description>Bidirectional one-phase energy meter connected via Modbus.</description>
<channels>
<channel id="total_energy" typeId="total_energy"/>
<channel id="feeding_back_energy" typeId="feeding_back_energy"/>
<channel id="voltage" typeId="voltage"/>
<channel id="current" typeId="current"/>
<channel id="active_power" typeId="active_power"/>
<channel id="reactive_power" typeId="reactive_power"/>
<channel id="power_factor" typeId="power_factor"/>
</channels>
<config-description-ref uri="thing-type:modbus-sbc:ald1"/>
</thing-type>
<channel-type id="total_energy">
<item-type>Number:Energy</item-type>
<label>Energy Total</label>
<state pattern="%.2f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="partial_energy">
<item-type>Number:Energy</item-type>
<label>Energy Counter Resettable</label>
<state pattern="%.2f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="feeding_back_energy">
<item-type>Number:Energy</item-type>
<label>Energy Feeding Back</label>
<state pattern="%.2f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="voltage">
<item-type>Number:ElectricPotential</item-type>
<label>Effective Voltage</label>
<state pattern="%.0f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="current">
<item-type>Number:ElectricCurrent</item-type>
<label>Effective Current</label>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="active_power">
<item-type>Number:Power</item-type>
<label>Effective Active Power</label>
<description>Negative numbers mean feeding back</description>
<state pattern="%.2f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="reactive_power">
<item-type>Number:Power</item-type>
<label>Effective Reactive Power</label>
<description>Negative numbers mean feeding back</description>
<state pattern="%.2f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="power_factor">
<item-type>Number:Dimensionless</item-type>
<label>Power Factor</label>
<state pattern="%.2f %unit%" readOnly="true"/>
</channel-type>
</thing:thing-descriptions>