added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.modbus.e3dc-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>file:${basedirRoot}/bundles/org.openhab.io.transport.modbus/target/feature/feature.xml</repository>
<feature name="openhab-binding-modbus-e3dc" description="E3DC Modbus Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<feature>openhab-transport-modbus</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.modbus/${project.version}</bundle>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.modbus.e3dc/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,92 @@
/**
* Copyright (c) 2010-2020 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.e3dc.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.modbus.ModbusBindingConstants;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link E3DCBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class E3DCBindingConstants {
private static final String BINDING_ID = ModbusBindingConstants.BINDING_ID;
// Supported Thing Types
public static final ThingTypeUID THING_TYPE_E3DC = new ThingTypeUID(BINDING_ID, "e3dc");
public static final ThingTypeUID THING_TYPE_E3DC_WALLBOX = new ThingTypeUID(BINDING_ID, "e3dc-wallbox");
// Channels for Info Block
public static final String MODBUS_ID_CHANNEL = "modbus-id";
public static final String MODBUS_FIRMWARE_CHANNEL = "modbus-firmware";
public static final String SUPPORTED_REGISTERS_CHANNEL = "supported-registers";
public static final String MANUFACTURER_NAME_CHANNEL = "manufacturer-name";
public static final String MODEL_NAME_CHANNEL = "model-name";
public static final String SERIAL_NUMBER_CHANNEL = "serial-number";
public static final String FIRMWARE_RELEASE_CHANNEL = "firmware-release";
// Channels for Power Block
public static final String PV_POWER_SUPPLY_CHANNEL = "pv-power-supply";
public static final String BATTERY_POWER_SUPPLY_CHANNEL = "battery-power-supply";
public static final String BATTERY_POWER_CONSUMPTION = "battery-power-consumption";
public static final String HOUSEHOLD_POWER_CONSUMPTION_CHANNEL = "household-power-consumption";
public static final String GRID_POWER_CONSUMPTION_CHANNEL = "grid-power-consumption";
public static final String GRID_POWER_SUPPLY_CHANNEL = "grid-power-supply";
public static final String EXTERNAL_POWER_SUPPLY_CHANNEL = "external-power-supply";
public static final String WALLBOX_POWER_CONSUMPTION_CHANNEL = "wallbox-power-consumption";
public static final String WALLBOX_PV_POWER_CONSUMPTION_CHANNEL = "wallbox-pv-power-consumption";
public static final String AUTARKY_CHANNEL = "autarky";
public static final String SELF_CONSUMPTION_CHANNEL = "self-consumption";
public static final String BATTERY_STATE_OF_CHARGE_CHANNEL = "battery-soc";
// Channels for Wallbox Block
public static final String WB_AVAILABLE_CHANNEL = "wb-available";
public static final String WB_SUNMODE_CHANNEL = "wb-sunmode";
public static final String WB_CHARGING_ABORTED_CHANNEL = "wb-charging-aborted";
public static final String WB_CHARGING_CHANNEL = "wb-charging";
public static final String WB_JACK_LOCKED_CHANNEL = "wb-jack-locked";
public static final String WB_JACK_PLUGGED_CHANNEL = "wb-jack-plugged";
public static final String WB_SCHUKO_ON_CHANNEL = "wb-schuko-on";
public static final String WB_SCHUKO_PLUGGED_CHANNEL = "wb-schuko-plugged";
public static final String WB_SCHUKO_LOCKED_CHANNEL = "wb-schuko-locked";
public static final String WB_SCHUKO_RELAY_16A_CHANNEL = "wb-schuko-relay-16a";
public static final String WB_RELAY_16A_CHANNEL = "wb-relay-16a";
public static final String WB_RELAY_32A_CHANNEL = "wb-relay-32a";
public static final String WB_1PHASE_CHANNEL = "wb-1phase";
// Channels for String details
public static final String STRING1_DC_VOLTAGE_CHANNEL = "string1-dc-voltage";
public static final String STRING1_DC_CURRENT_CHANNEL = "string1-dc-current";
public static final String STRING1_DC_OUTPUT_CHANNEL = "string1-dc-output";
public static final String STRING2_DC_VOLTAGE_CHANNEL = "string2-dc-voltage";
public static final String STRING2_DC_CURRENT_CHANNEL = "string2-dc-current";
public static final String STRING2_DC_OUTPUT_CHANNEL = "string2-dc-output";
public static final String STRING3_DC_VOLTAGE_CHANNEL = "string3-dc-voltage";
public static final String STRING3_DC_CURRENT_CHANNEL = "string3-dc-current";
public static final String STRING3_DC_OUTPUT_CHANNEL = "string3-dc-output";
// Channels for Emergency Status
public static final String EMERGENCY_POWER_STATUS = "emergency-power-status";
public static final String BATTERY_CHARGING_LOCKED = "battery-charging-lock";
public static final String BATTERY_DISCHARGING_LOCKED = "battery-discharging-lock";
public static final String EMERGENCY_POWER_POSSIBLE = "emergency-power-possible";
public static final String WEATHER_PREDICTED_CHARGING = "weather-predicted-charging";
public static final String REGULATION_STATUS = "regulation-status";
public static final String CHARGE_LOCK_TIME = "charge-lock-time";
public static final String DISCHARGE_LOCK_TIME = "discharge-lock-time";
}

View File

@@ -0,0 +1,29 @@
/**
* Copyright (c) 2010-2020 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.e3dc.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link E3DCConfiguration} class contains fields mapping thing configuration parameters.
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class E3DCConfiguration {
/**
* Data refresh interval
*/
public int refresh = 2000;
}

View File

@@ -0,0 +1,52 @@
/**
* Copyright (c) 2010-2020 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.e3dc.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.modbus.e3dc.internal.handler.E3DCThingHandler;
import org.openhab.binding.modbus.e3dc.internal.handler.E3DCWallboxThingHandler;
import org.openhab.core.thing.Bridge;
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 E3DCHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
@Component(configurationPid = "binding.e3dc", service = ThingHandlerFactory.class)
public class E3DCHandlerFactory extends BaseThingHandlerFactory {
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return (thingTypeUID.equals(E3DCBindingConstants.THING_TYPE_E3DC)
|| thingTypeUID.equals(E3DCBindingConstants.THING_TYPE_E3DC_WALLBOX));
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (E3DCBindingConstants.THING_TYPE_E3DC.equals(thingTypeUID)) {
return new E3DCThingHandler((Bridge) thing);
} else if (E3DCBindingConstants.THING_TYPE_E3DC_WALLBOX.equals(thingTypeUID)) {
return new E3DCWallboxThingHandler(thing);
}
return null;
}
}

View File

@@ -0,0 +1,29 @@
/**
* Copyright (c) 2010-2020 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.e3dc.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link E3DCWallboxConfiguration} class contains fields mapping thing configuration parameters.
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class E3DCWallboxConfiguration {
/**
* Wallbox ID
*/
public int wallboxId = -1;
}

View File

@@ -0,0 +1,100 @@
/**
* Copyright (c) 2010-2020 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.e3dc.internal.dto;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.BitSet;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link DataConverter} Helper class to convert bytes from modbus into desired data format
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class DataConverter {
private static final long MAX_INT32 = (long) Math.pow(2, Integer.SIZE);
/**
* Get unit16 value from 2 bytes
*
* @param wrap
* @return int
*/
public static int getUInt16Value(ByteBuffer wrap) {
return Short.toUnsignedInt(wrap.getShort());
}
/**
* Get unit32 value from 4 bytes
*
* @param wrap
* @return long
*/
public static long getLongValue(ByteBuffer wrap) {
return Integer.toUnsignedLong(wrap.getInt());
}
/**
* Get double value from 2 bytes with correction factor
*
* @param wrap
* @return double
*/
public static double getUDoubleValue(ByteBuffer wrap, double factor) {
return round(getUInt16Value(wrap) * factor, 2);
}
/**
* Conversion done according to E3DC Modbus Specification V1.7
*
* @param wrap
* @return decoded long value, Long.MIN_VALUE otherwise
*/
public static long getInt32Swap(ByteBuffer wrap) {
long a = getUInt16Value(wrap);
long b = getUInt16Value(wrap);
if (b < 32768) {
return b * 65536 + a;
} else {
return (MAX_INT32 - b * 65536 - a) * -1;
}
}
public static String getString(byte[] bArray) {
return new String(bArray, StandardCharsets.US_ASCII).trim();
}
public static int toInt(BitSet bitSet) {
int intValue = 0;
for (int bit = 0; bit < bitSet.length(); bit++) {
if (bitSet.get(bit)) {
intValue |= (1 << bit);
}
}
return intValue;
}
public static double round(double value, int places) {
if (places < 0) {
throw new IllegalArgumentException();
}
long factor = (long) Math.pow(10, places);
value = value * factor;
long tmp = Math.round(value);
return (double) tmp / factor;
}
}

View File

@@ -0,0 +1,76 @@
/**
* Copyright (c) 2010-2020 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.e3dc.internal.dto;
import static org.openhab.binding.modbus.e3dc.internal.modbus.E3DCModbusConstans.*;
import java.nio.ByteBuffer;
import java.util.BitSet;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.modbus.e3dc.internal.modbus.Data;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
/**
* The {@link EmergencyBlock} Data object for E3DC Info Block
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class EmergencyBlock implements Data {
public StringType epStatus = EP_UNKOWN;
public OnOffType batteryChargingLocked = OnOffType.OFF;
public OnOffType batteryDischargingLocked = OnOffType.OFF;
public OnOffType epPossible = OnOffType.OFF;
public OnOffType weatherPredictedCharging = OnOffType.OFF;
public OnOffType regulationStatus = OnOffType.OFF;
public OnOffType chargeLockTime = OnOffType.OFF;
public OnOffType dischargeLockTime = OnOffType.OFF;
// Possible Status definitions according to chapter 3.1.2, Register 40084, page 14 & 15
public static final StringType EP_NOT_SUPPORTED = StringType.valueOf("EP not supported");
public static final StringType EP_ACTIVE = StringType.valueOf("EP active");
public static final StringType EP_NOT_ACTIVE = StringType.valueOf("EP not active");
public static final StringType EP_POSSIBLE = StringType.valueOf("EP possible");
public static final StringType EP_SWITCH = StringType.valueOf("EP Switch in wrong position");
public static final StringType EP_UNKOWN = StringType.valueOf("EP Status unknown");
public static final StringType[] EP_STATUS_ARRAY = new StringType[] { EP_NOT_SUPPORTED, EP_ACTIVE, EP_NOT_ACTIVE,
EP_POSSIBLE, EP_SWITCH };
/**
* For decoding see Modbus Register Mapping Chapter 3.1.2 page 14 & 15
*
* @param bArray - Modbus Registers as bytes from 40084 to 40085
*/
public EmergencyBlock(byte[] bArray) {
// uint16 status register 40084 - possible Status Strings are defined in Constants above
int status = DataConverter.getUInt16Value(ByteBuffer.wrap(bArray));
if (status >= 0 && status < 5) {
epStatus = EP_STATUS_ARRAY[status];
} else {
epStatus = EP_UNKOWN;
}
// uint16 status register 40085 shall be handled as Bits - check cahpter 3.1.3 page 17
byte[] emsStatusBytes = new byte[] { bArray[3], bArray[2] };
BitSet bs = BitSet.valueOf(emsStatusBytes);
batteryChargingLocked = OnOffType.from(bs.get(EMS_CHARGING_LOCK_BIT));
batteryDischargingLocked = OnOffType.from(bs.get(EMS_DISCHARGING_LOCK_BIT));
epPossible = OnOffType.from(bs.get(EMS_DISCHARGING_LOCK_BIT));
weatherPredictedCharging = OnOffType.from(bs.get(EMS_WEATHER_CHARGING_BIT));
regulationStatus = OnOffType.from(bs.get(EMS_REGULATION_BIT));
chargeLockTime = OnOffType.from(bs.get(EMS_CHARGE_LOCKTIME_BIT));
dischargeLockTime = OnOffType.from(bs.get(EMS_DISCHARGE_LOCKTIME_BIT));
}
}

View File

@@ -0,0 +1,83 @@
/**
* Copyright (c) 2010-2020 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.e3dc.internal.dto;
import java.nio.ByteBuffer;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.modbus.e3dc.internal.modbus.Data;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.util.HexUtils;
/**
* The {@link InfoBlock} Data object for E3DC Info Block
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class InfoBlock implements Data {
private static final StringType EMPTY = new StringType("NULL");
public StringType modbusId = EMPTY;
public StringType modbusVersion = EMPTY;
public DecimalType supportedRegisters = new DecimalType(-1);
public StringType manufacturer = EMPTY;
public StringType modelName = EMPTY;
public StringType serialNumber = EMPTY;
public StringType firmware = EMPTY;
/**
* For decoding see Modbus Register Mapping Chapter 3.1.1 page 14
*
* @param bArray - Modbus Registers as bytes from 40001 to 40067
*/
public InfoBlock(byte[] bArray) {
// index handling to calculate the correct start index
ByteBuffer wrapper = ByteBuffer.wrap(bArray);
// first uint16 = 2 bytes - decode magic byte
byte[] magicBytes = new byte[2];
wrapper.get(magicBytes);
this.modbusId = new StringType(HexUtils.bytesToHex(magicBytes));
// first uint16 = 2 bytes - decode magic byte
// unit8 (Modbus Major Version) + uint8 Modbus minor Version
String modbusVersion = wrapper.get() + "." + wrapper.get();
this.modbusVersion = new StringType(modbusVersion);
// unit16 - supported registers
short supportedRegisters = wrapper.getShort();
this.supportedRegisters = new DecimalType(supportedRegisters);
byte[] buffer = new byte[32];
// 16 registers with uint16 = 32 bytes to decode a proper String
wrapper.get(buffer);
String manufacturer = DataConverter.getString(buffer);
this.manufacturer = new StringType(manufacturer);
// 16 registers with uint16 = 32 bytes to decode a proper String
wrapper.get(buffer);
String model = DataConverter.getString(buffer);
this.modelName = new StringType(model);
// 16 registers with uint16 = 32 bytes to decode a proper String
wrapper.get(buffer);
String serialNumber = DataConverter.getString(buffer);
this.serialNumber = new StringType(serialNumber);
// 16 registers with uint16 = 32 bytes to decode a proper String
wrapper.get(buffer);
String firmware = DataConverter.getString(buffer);
this.firmware = new StringType(firmware);
}
}

View File

@@ -0,0 +1,110 @@
/**
* Copyright (c) 2010-2020 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.e3dc.internal.dto;
import java.nio.ByteBuffer;
import javax.measure.quantity.Dimensionless;
import javax.measure.quantity.Power;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.modbus.e3dc.internal.modbus.Data;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.SmartHomeUnits;
/**
* The {@link PowerBlock} Data object for E3DC Info Block
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class PowerBlock implements Data {
public QuantityType<Power> pvPowerSupply;
public QuantityType<Power> batteryPowerSupply;
public QuantityType<Power> batteryPowerConsumption;
public QuantityType<Power> householdPowerConsumption;
public QuantityType<Power> gridPowerConsumpition;
public QuantityType<Power> gridPowerSupply;
public QuantityType<Power> externalPowerSupply;
public QuantityType<Power> wallboxPowerConsumption;
public QuantityType<Power> wallboxPVPowerConsumption;
public QuantityType<Dimensionless> autarky;
public QuantityType<Dimensionless> selfConsumption;
public QuantityType<Dimensionless> batterySOC;
/**
* For decoding see Modbus Register Mapping Chapter 3.1.2 page 14
*
* @param bArray - Modbus Registers as bytes from 40067 to 40083
*/
public PowerBlock(byte[] bArray) {
// index handling to calculate the correct start index
ByteBuffer wrap = ByteBuffer.wrap(bArray);
// int32_swap value = 4 byte
long pvPowerSupplyL = DataConverter.getInt32Swap(wrap);
/*
* int32_swap value don't provide negative values!
* Positive value - Battery is charging = Power consumer
* Negative value - Battery is discharging = Power supplier
*/
pvPowerSupply = QuantityType.valueOf(pvPowerSupplyL, SmartHomeUnits.WATT);
long batteryPower = DataConverter.getInt32Swap(wrap);
if (batteryPower > 0) {
// Battery is charging so Power is consumed by Battery
batteryPowerSupply = QuantityType.valueOf(0, SmartHomeUnits.WATT);
batteryPowerConsumption = QuantityType.valueOf(batteryPower, SmartHomeUnits.WATT);
} else {
// Battery is discharging so Power is provided by Battery
batteryPowerSupply = QuantityType.valueOf(batteryPower * -1, SmartHomeUnits.WATT);
batteryPowerConsumption = QuantityType.valueOf(0, SmartHomeUnits.WATT);
}
// int32_swap value = 4 byte
long householdPowerConsumptionL = DataConverter.getInt32Swap(wrap);
householdPowerConsumption = QuantityType.valueOf(householdPowerConsumptionL, SmartHomeUnits.WATT);
/*
* int32_swap value don't provide negative values!
* Positive value - Power provided towards Grid = Power consumer
* Negative value - Power requested from Grid = Power supplier
*/
long gridPower = DataConverter.getInt32Swap(wrap);
if (gridPower > 0) {
// Power is provided by Grid
gridPowerSupply = QuantityType.valueOf(gridPower, SmartHomeUnits.WATT);
gridPowerConsumpition = QuantityType.valueOf(0, SmartHomeUnits.WATT);
} else {
// Power is consumed by Grid
gridPowerConsumpition = QuantityType.valueOf(gridPower * -1, SmartHomeUnits.WATT);
gridPowerSupply = QuantityType.valueOf(0, SmartHomeUnits.WATT);
}
// int32_swap value = 4 byte
externalPowerSupply = QuantityType.valueOf(DataConverter.getInt32Swap(wrap), SmartHomeUnits.WATT);
// int32_swap value = 4 byte
wallboxPowerConsumption = QuantityType.valueOf(DataConverter.getInt32Swap(wrap), SmartHomeUnits.WATT);
// int32_swap value = 4 byte
wallboxPVPowerConsumption = QuantityType.valueOf(DataConverter.getInt32Swap(wrap), SmartHomeUnits.WATT);
// unit8 + uint8 - one register with split value for Autarky & Self Consumption
autarky = QuantityType.valueOf(wrap.get(), SmartHomeUnits.PERCENT);
selfConsumption = QuantityType.valueOf(wrap.get(), SmartHomeUnits.PERCENT);
// uint16 for Battery State of Charge
batterySOC = QuantityType.valueOf(wrap.getShort(), SmartHomeUnits.PERCENT);
}
}

View File

@@ -0,0 +1,62 @@
/**
* Copyright (c) 2010-2020 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.e3dc.internal.dto;
import java.nio.ByteBuffer;
import javax.measure.quantity.ElectricCurrent;
import javax.measure.quantity.ElectricPotential;
import javax.measure.quantity.Power;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.modbus.e3dc.internal.modbus.Data;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.SmartHomeUnits;
/**
* The {@link StringBlock} Data object for E3DC Info Block
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class StringBlock implements Data {
public QuantityType<ElectricPotential> string1Volt;
public QuantityType<ElectricPotential> string2Volt;
public QuantityType<ElectricPotential> string3Volt;
public QuantityType<ElectricCurrent> string1Ampere;
public QuantityType<ElectricCurrent> string2Ampere;
public QuantityType<ElectricCurrent> string3Ampere;
public QuantityType<Power> string1Watt;
public QuantityType<Power> string2Watt;
public QuantityType<Power> string3Watt;
/**
* For decoding see Modbus Register Mapping Chapter 3.1.2 page 14-16
*
* @param bArray - Modbus Registers as bytes from 40096 to 40104
*/
public StringBlock(byte[] bArray) {
ByteBuffer wrap = ByteBuffer.wrap(bArray);
// straight forward - for each String the values Volt, Ampere and then Watt. All unt16 = 2 bytes values
string1Volt = QuantityType.valueOf(DataConverter.getUInt16Value(wrap), SmartHomeUnits.VOLT);
string2Volt = QuantityType.valueOf(DataConverter.getUInt16Value(wrap), SmartHomeUnits.VOLT);
string3Volt = QuantityType.valueOf(DataConverter.getUInt16Value(wrap), SmartHomeUnits.VOLT);
// E3DC Modbus Spec chapter 3.1.2, page 16 - Ampere values shall be handled with factor 0.01
string1Ampere = QuantityType.valueOf(DataConverter.getUDoubleValue(wrap, 0.01), SmartHomeUnits.AMPERE);
string2Ampere = QuantityType.valueOf(DataConverter.getUDoubleValue(wrap, 0.01), SmartHomeUnits.AMPERE);
string3Ampere = QuantityType.valueOf(DataConverter.getUDoubleValue(wrap, 0.01), SmartHomeUnits.AMPERE);
string1Watt = QuantityType.valueOf(DataConverter.getUInt16Value(wrap), SmartHomeUnits.WATT);
string2Watt = QuantityType.valueOf(DataConverter.getUInt16Value(wrap), SmartHomeUnits.WATT);
string3Watt = QuantityType.valueOf(DataConverter.getUInt16Value(wrap), SmartHomeUnits.WATT);
}
}

View File

@@ -0,0 +1,54 @@
/**
* Copyright (c) 2010-2020 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.e3dc.internal.dto;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.modbus.e3dc.internal.modbus.Data;
/**
* The {@link WallboxArray} Data object for E3DC Info Block
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class WallboxArray implements Data {
private byte[] wbArray;
/**
* For decoding see Modbus Register Mapping Chapter 3.1.2 page 15
* The Registers for Wallbox Control are declared as uint16 but shall be handled as Bit registers => see chapter
* 3.1.5 page 19
*
* @param bArray - Modbus Registers as bytes from 40088 to 40095
*/
public WallboxArray(byte[] bArray) {
wbArray = bArray;
}
/**
* Return the 2 bytes according to the Wallbox ID.
*
* @param id Wallbox ID valid from 0 - 7
* @return WallboxBlock initialized with the Modbus registers from the given ID
*/
public Optional<WallboxBlock> getWallboxBlock(int id) {
if (id >= 0 && id < 8) {
int byteIndex = id * 2;
return Optional.of(new WallboxBlock(new byte[] { wbArray[byteIndex + 1], wbArray[byteIndex * 2] }));
} else {
return Optional.empty();
}
}
}

View File

@@ -0,0 +1,72 @@
/**
* Copyright (c) 2010-2020 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.e3dc.internal.dto;
import static org.openhab.binding.modbus.e3dc.internal.modbus.E3DCModbusConstans.*;
import java.util.BitSet;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.modbus.e3dc.internal.modbus.Data;
import org.openhab.core.library.types.OnOffType;
/**
* The {@link WallboxBlock} Data object for E3DC Info Block
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class WallboxBlock implements Data {
private BitSet bitSet;
public OnOffType wbAvailable = OnOffType.OFF;
public OnOffType wbSunmode = OnOffType.OFF;
public OnOffType wbChargingAborted = OnOffType.OFF;
public OnOffType wbCharging = OnOffType.OFF;
public OnOffType wbJackLocked = OnOffType.OFF;
public OnOffType wbJackPlugged = OnOffType.OFF;
public OnOffType wbSchukoOn = OnOffType.OFF;
public OnOffType wbSchukoPlugged = OnOffType.OFF;
public OnOffType wbSchukoLocked = OnOffType.OFF;
public OnOffType wbSchukoRelay16 = OnOffType.OFF;
public OnOffType wbRelay16 = OnOffType.OFF;
public OnOffType wbRelay32 = OnOffType.OFF;
public OnOffType wb1phase = OnOffType.OFF;
/**
* For decoding see Modbus Register Mapping Chapter 3.1.2 page 15
* The Registers for Wallbox Control are declared as uint16 but shall be handled as Bit registers => see chapter
* 3.1.5 page 19
*
* @param bArray - one Modbus Registers according to Wallbox ID
*/
public WallboxBlock(byte[] bArray) {
bitSet = BitSet.valueOf(bArray);
wbAvailable = OnOffType.from(bitSet.get(WB_AVAILABLE_BIT));
wbSunmode = OnOffType.from(bitSet.get(WB_SUNMODE_BIT));
wbChargingAborted = OnOffType.from(bitSet.get(WB_CHARGING_ABORTED_BIT));
wbCharging = OnOffType.from(bitSet.get(WB_CHARGING_BIT));
wbJackLocked = OnOffType.from(bitSet.get(WB_JACK_LOCKED_BIT));
wbJackPlugged = OnOffType.from(bitSet.get(WB_JACK_PLUGGED_BIT));
wbSchukoOn = OnOffType.from(bitSet.get(WB_SCHUKO_ON_BIT));
wbSchukoPlugged = OnOffType.from(bitSet.get(WB_SCHUKO_PLUGGED_BIT));
wbSchukoLocked = OnOffType.from(bitSet.get(WB_SCHUKO_LOCKED_BIT));
wbSchukoRelay16 = OnOffType.from(bitSet.get(WB_SCHUKO_RELAY16A_BIT));
wbRelay16 = OnOffType.from(bitSet.get(WB_RELAY_16A_BIT));
wbRelay32 = OnOffType.from(bitSet.get(WB_RELAY_32A_BIT));
wb1phase = OnOffType.from(bitSet.get(WB_1PHASE_BIT));
}
public BitSet getBitSet() {
return bitSet;
}
}

View File

@@ -0,0 +1,450 @@
/**
* Copyright (c) 2010-2020 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.e3dc.internal.handler;
import static org.openhab.binding.modbus.e3dc.internal.E3DCBindingConstants.*;
import static org.openhab.binding.modbus.e3dc.internal.modbus.E3DCModbusConstans.*;
import java.util.ArrayList;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.modbus.e3dc.internal.E3DCConfiguration;
import org.openhab.binding.modbus.e3dc.internal.dto.EmergencyBlock;
import org.openhab.binding.modbus.e3dc.internal.dto.InfoBlock;
import org.openhab.binding.modbus.e3dc.internal.dto.PowerBlock;
import org.openhab.binding.modbus.e3dc.internal.dto.StringBlock;
import org.openhab.binding.modbus.e3dc.internal.modbus.Data;
import org.openhab.binding.modbus.e3dc.internal.modbus.Data.DataType;
import org.openhab.binding.modbus.e3dc.internal.modbus.Parser;
import org.openhab.binding.modbus.handler.EndpointNotInitializedException;
import org.openhab.binding.modbus.handler.ModbusEndpointThingHandler;
import org.openhab.core.thing.Bridge;
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.thing.binding.BaseBridgeHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.types.Command;
import org.openhab.io.transport.modbus.AsyncModbusFailure;
import org.openhab.io.transport.modbus.AsyncModbusReadResult;
import org.openhab.io.transport.modbus.ModbusCommunicationInterface;
import org.openhab.io.transport.modbus.ModbusReadFunctionCode;
import org.openhab.io.transport.modbus.ModbusReadRequestBlueprint;
import org.openhab.io.transport.modbus.PollTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link E3DCThingHandler} Basic modbus connection towards the E3DC device
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class E3DCThingHandler extends BaseBridgeHandler {
public enum ReadStatus {
NOT_RECEIVED,
READ_SUCCESS,
READ_FAILED
}
static final String INFO_DATA_READ_ERROR = "Information And Data Modbus Read Errors";
static final String INFO_READ_ERROR = "Information Modbus Read Error";
static final String DATA_READ_ERROR = "Data Modbus Read Error";
static final String INFO_GROUP = "info";
static final String EMERGENCY_GROUP = "emergency";
static final String POWER_GROUP = "power";
static final String STRINGS_GROUP = "strings";
private ChannelUID modbusIdChannel;
private ChannelUID modbusVersionChannel;
private ChannelUID supportedRegistersChannel;
private ChannelUID manufacturerChannel;
private ChannelUID modelNameChannel;
private ChannelUID serialNumberChannel;
private ChannelUID firmwareChannel;
private ChannelUID epStatusChannel;
private ChannelUID batteryChargingLockedChannel;
private ChannelUID batteryDischargingLockedChannel;
private ChannelUID epPossibleChannel;
private ChannelUID weatherPredictedChargingChannel;
private ChannelUID regulationStatusChannel;
private ChannelUID chargeLockTimeChannel;
private ChannelUID dischargeLockTimeChannel;
private ChannelUID pvPowerSupplyChannel;
private ChannelUID batteryPowerSupplyChannel;
private ChannelUID batteryPowerConsumptionChannel;
private ChannelUID householdPowerConsumptionChannel;
private ChannelUID gridPowerConsumpitionChannel;
private ChannelUID gridPowerSupplyChannel;
private ChannelUID externalPowerSupplyChannel;
private ChannelUID wallboxPowerConsumptionChannel;
private ChannelUID wallboxPVPowerConsumptionChannel;
private ChannelUID autarkyChannel;
private ChannelUID selfConsumptionChannel;
private ChannelUID batterySOCChannel;
private ChannelUID string1AmpereChannel;
private ChannelUID string1VoltChannel;
private ChannelUID string1WattChannel;
private ChannelUID string2AmpereChannel;
private ChannelUID string2VoltChannel;
private ChannelUID string2WattChannel;
private ChannelUID string3AmpereChannel;
private ChannelUID string3VoltChannel;
private ChannelUID string3WattChannel;
private final ArrayList<E3DCWallboxThingHandler> listeners = new ArrayList<E3DCWallboxThingHandler>();
private final Logger logger = LoggerFactory.getLogger(E3DCThingHandler.class);
private final Parser dataParser = new Parser(DataType.DATA);
private ReadStatus dataRead = ReadStatus.NOT_RECEIVED;
private final Parser infoParser = new Parser(DataType.INFO);
private ReadStatus infoRead = ReadStatus.NOT_RECEIVED;
private @Nullable PollTask infoPoller;
private @Nullable PollTask dataPoller;
private @Nullable E3DCConfiguration config;
/**
* Communication interface to the slave endpoint we're connecting to
*/
protected volatile @Nullable ModbusCommunicationInterface comms = null;
private int slaveId;
public E3DCThingHandler(Bridge thing) {
super(thing);
modbusIdChannel = channelUID(thing, INFO_GROUP, MODBUS_ID_CHANNEL);
modbusVersionChannel = channelUID(thing, INFO_GROUP, MODBUS_FIRMWARE_CHANNEL);
supportedRegistersChannel = channelUID(thing, INFO_GROUP, SUPPORTED_REGISTERS_CHANNEL);
manufacturerChannel = channelUID(thing, INFO_GROUP, MANUFACTURER_NAME_CHANNEL);
modelNameChannel = channelUID(thing, INFO_GROUP, MODEL_NAME_CHANNEL);
serialNumberChannel = channelUID(thing, INFO_GROUP, SERIAL_NUMBER_CHANNEL);
firmwareChannel = channelUID(thing, INFO_GROUP, FIRMWARE_RELEASE_CHANNEL);
epStatusChannel = channelUID(thing, EMERGENCY_GROUP, EMERGENCY_POWER_STATUS);
batteryChargingLockedChannel = channelUID(thing, EMERGENCY_GROUP, BATTERY_CHARGING_LOCKED);
batteryDischargingLockedChannel = channelUID(thing, EMERGENCY_GROUP, BATTERY_DISCHARGING_LOCKED);
epPossibleChannel = channelUID(thing, EMERGENCY_GROUP, EMERGENCY_POWER_POSSIBLE);
weatherPredictedChargingChannel = channelUID(thing, EMERGENCY_GROUP, WEATHER_PREDICTED_CHARGING);
regulationStatusChannel = channelUID(thing, EMERGENCY_GROUP, REGULATION_STATUS);
chargeLockTimeChannel = channelUID(thing, EMERGENCY_GROUP, CHARGE_LOCK_TIME);
dischargeLockTimeChannel = channelUID(thing, EMERGENCY_GROUP, DISCHARGE_LOCK_TIME);
pvPowerSupplyChannel = channelUID(thing, POWER_GROUP, PV_POWER_SUPPLY_CHANNEL);
batteryPowerSupplyChannel = channelUID(thing, POWER_GROUP, BATTERY_POWER_SUPPLY_CHANNEL);
batteryPowerConsumptionChannel = channelUID(thing, POWER_GROUP, BATTERY_POWER_CONSUMPTION);
householdPowerConsumptionChannel = channelUID(thing, POWER_GROUP, HOUSEHOLD_POWER_CONSUMPTION_CHANNEL);
gridPowerConsumpitionChannel = channelUID(thing, POWER_GROUP, GRID_POWER_CONSUMPTION_CHANNEL);
gridPowerSupplyChannel = channelUID(thing, POWER_GROUP, GRID_POWER_SUPPLY_CHANNEL);
externalPowerSupplyChannel = channelUID(thing, POWER_GROUP, EXTERNAL_POWER_SUPPLY_CHANNEL);
wallboxPowerConsumptionChannel = channelUID(thing, POWER_GROUP, WALLBOX_POWER_CONSUMPTION_CHANNEL);
wallboxPVPowerConsumptionChannel = channelUID(thing, POWER_GROUP, WALLBOX_PV_POWER_CONSUMPTION_CHANNEL);
autarkyChannel = channelUID(thing, POWER_GROUP, AUTARKY_CHANNEL);
selfConsumptionChannel = channelUID(thing, POWER_GROUP, SELF_CONSUMPTION_CHANNEL);
batterySOCChannel = channelUID(thing, POWER_GROUP, BATTERY_STATE_OF_CHARGE_CHANNEL);
string1AmpereChannel = channelUID(thing, STRINGS_GROUP, STRING1_DC_CURRENT_CHANNEL);
string1VoltChannel = channelUID(thing, STRINGS_GROUP, STRING1_DC_VOLTAGE_CHANNEL);
string1WattChannel = channelUID(thing, STRINGS_GROUP, STRING1_DC_OUTPUT_CHANNEL);
string2AmpereChannel = channelUID(thing, STRINGS_GROUP, STRING2_DC_CURRENT_CHANNEL);
string2VoltChannel = channelUID(thing, STRINGS_GROUP, STRING2_DC_VOLTAGE_CHANNEL);
string2WattChannel = channelUID(thing, STRINGS_GROUP, STRING2_DC_OUTPUT_CHANNEL);
string3AmpereChannel = channelUID(thing, STRINGS_GROUP, STRING3_DC_CURRENT_CHANNEL);
string3VoltChannel = channelUID(thing, STRINGS_GROUP, STRING3_DC_VOLTAGE_CHANNEL);
string3WattChannel = channelUID(thing, STRINGS_GROUP, STRING3_DC_OUTPUT_CHANNEL);
}
public @Nullable ModbusCommunicationInterface getComms() {
return comms;
}
public int getSlaveId() {
return slaveId;
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
// no control of E3DC device possible yet
}
@Override
public void initialize() {
updateStatus(ThingStatus.UNKNOWN);
scheduler.execute(() -> {
E3DCConfiguration localConfig = getConfigAs(E3DCConfiguration.class);
config = localConfig;
ModbusCommunicationInterface localComms = connectEndpoint();
if (localComms != null) {
// register low speed info poller
ModbusReadRequestBlueprint infoRequest = new ModbusReadRequestBlueprint(slaveId,
ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS, INFO_REG_START, INFO_REG_SIZE, 3);
infoPoller = localComms.registerRegularPoll(infoRequest, INFO_POLL_REFRESH_TIME_MS, 0,
this::handleInfoResult, this::handleInfoFailure);
ModbusReadRequestBlueprint dataRequest = new ModbusReadRequestBlueprint(slaveId,
ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS, POWER_REG_START,
REGISTER_LENGTH - INFO_REG_SIZE, 3);
if (config != null) {
dataPoller = localComms.registerRegularPoll(dataRequest, localConfig.refresh, 0,
this::handleDataResult, this::handleDataFailure);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"E3DC Configuration missing");
}
} // else state handling performed in connectEndPoint function
});
}
/**
* Get a reference to the modbus endpoint
*/
private @Nullable ModbusCommunicationInterface connectEndpoint() {
if (comms != null) {
return comms;
}
ModbusEndpointThingHandler slaveEndpointThingHandler = getEndpointThingHandler();
if (slaveEndpointThingHandler == null) {
@SuppressWarnings("null")
String label = Optional.ofNullable(getBridge()).map(b -> b.getLabel()).orElse("<null>");
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE,
String.format("Bridge '%s' is offline", label));
return null;
}
try {
slaveId = slaveEndpointThingHandler.getSlaveId();
comms = slaveEndpointThingHandler.getCommunicationInterface();
} catch (EndpointNotInitializedException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
String.format("Slave Endpoint not initialized"));
return null;
}
if (comms == null) {
@SuppressWarnings("null")
String label = Optional.ofNullable(getBridge()).map(b -> b.getLabel()).orElse("<null>");
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE,
String.format("Bridge '%s' not completely initialized", label));
return null;
} else {
return comms;
}
}
/**
* Get the endpoint handler from the bridge this handler is connected to
* Checks that we're connected to the right type of bridge
*
* @return the endpoint handler or null if the bridge does not exist
*/
private @Nullable ModbusEndpointThingHandler getEndpointThingHandler() {
Bridge bridge = getBridge();
if (bridge == null) {
logger.debug("Bridge is null");
return null;
}
if (bridge.getStatus() != ThingStatus.ONLINE) {
logger.debug("Bridge is not online");
return null;
}
ThingHandler handler = bridge.getHandler();
if (handler == null) {
logger.debug("Bridge handler is null");
return null;
}
if (handler instanceof ModbusEndpointThingHandler) {
ModbusEndpointThingHandler slaveEndpoint = (ModbusEndpointThingHandler) handler;
return slaveEndpoint;
} else {
logger.debug("Unexpected bridge handler: {}", handler);
return null;
}
}
/**
* Returns the channel UID for the specified group and channel id
*
* @param string the channel group
* @param string the channel id in that group
* @return the globally unique channel uid
*/
private ChannelUID channelUID(Thing t, String group, String id) {
return new ChannelUID(t.getUID(), group, id);
}
void handleInfoResult(AsyncModbusReadResult result) {
if (infoRead != ReadStatus.READ_SUCCESS) {
// update status only if bit switches
infoRead = ReadStatus.READ_SUCCESS;
updateStatus();
}
infoParser.handle(result);
Optional<Data> blockOpt = infoParser.parse(DataType.INFO);
if (blockOpt.isPresent()) {
InfoBlock block = (InfoBlock) blockOpt.get();
updateState(modbusIdChannel, block.modbusId);
updateState(modbusVersionChannel, block.modbusVersion);
updateState(supportedRegistersChannel, block.supportedRegisters);
updateState(manufacturerChannel, block.manufacturer);
updateState(modelNameChannel, block.modelName);
updateState(serialNumberChannel, block.serialNumber);
updateState(firmwareChannel, block.firmware);
} else {
logger.debug("Unable to get {} from provider {}", DataType.INFO, dataParser.toString());
}
}
void handleInfoFailure(AsyncModbusFailure<ModbusReadRequestBlueprint> result) {
if (infoRead != ReadStatus.READ_FAILED) {
// update status only if bit switches
infoRead = ReadStatus.READ_FAILED;
updateStatus();
}
}
void handleDataResult(AsyncModbusReadResult result) {
if (dataRead != ReadStatus.READ_SUCCESS) {
// update status only if bit switches
dataRead = ReadStatus.READ_SUCCESS;
updateStatus();
}
dataParser.handle(result);
// Update channels in emergency group
{
Optional<Data> blockOpt = dataParser.parse(DataType.EMERGENCY);
if (blockOpt.isPresent()) {
EmergencyBlock block = (EmergencyBlock) blockOpt.get();
updateState(epStatusChannel, block.epStatus);
updateState(batteryChargingLockedChannel, block.batteryChargingLocked);
updateState(batteryDischargingLockedChannel, block.batteryDischargingLocked);
updateState(epPossibleChannel, block.epPossible);
updateState(weatherPredictedChargingChannel, block.weatherPredictedCharging);
updateState(regulationStatusChannel, block.regulationStatus);
updateState(chargeLockTimeChannel, block.chargeLockTime);
updateState(dischargeLockTimeChannel, block.dischargeLockTime);
} else {
logger.debug("Unable to get {} from provider {}", DataType.EMERGENCY, dataParser.toString());
}
}
// Update channels in power group
{
Optional<Data> blockOpt = dataParser.parse(DataType.POWER);
if (blockOpt.isPresent()) {
PowerBlock block = (PowerBlock) blockOpt.get();
updateState(pvPowerSupplyChannel, block.pvPowerSupply);
updateState(batteryPowerSupplyChannel, block.batteryPowerSupply);
updateState(batteryPowerConsumptionChannel, block.batteryPowerConsumption);
updateState(householdPowerConsumptionChannel, block.householdPowerConsumption);
updateState(gridPowerConsumpitionChannel, block.gridPowerConsumpition);
updateState(gridPowerSupplyChannel, block.gridPowerSupply);
updateState(externalPowerSupplyChannel, block.externalPowerSupply);
updateState(wallboxPowerConsumptionChannel, block.wallboxPowerConsumption);
updateState(wallboxPVPowerConsumptionChannel, block.wallboxPVPowerConsumption);
updateState(autarkyChannel, block.autarky);
updateState(selfConsumptionChannel, block.selfConsumption);
updateState(batterySOCChannel, block.batterySOC);
} else {
logger.debug("Unable to get {} from provider {}", DataType.POWER, dataParser.toString());
}
}
// Update channels in strings group
{
Optional<Data> blockOpt = dataParser.parse(DataType.STRINGS);
if (blockOpt.isPresent()) {
StringBlock block = (StringBlock) blockOpt.get();
updateState(string1AmpereChannel, block.string1Ampere);
updateState(string1VoltChannel, block.string1Volt);
updateState(string1WattChannel, block.string1Watt);
updateState(string2AmpereChannel, block.string2Ampere);
updateState(string2VoltChannel, block.string2Volt);
updateState(string2WattChannel, block.string2Watt);
updateState(string3AmpereChannel, block.string3Ampere);
updateState(string3VoltChannel, block.string3Volt);
updateState(string3WattChannel, block.string3Watt);
} else {
logger.debug("Unable to get {} from provider {}", DataType.STRINGS, dataParser.toString());
}
}
listeners.forEach(l -> {
l.handle(result);
});
}
void handleDataFailure(AsyncModbusFailure<ModbusReadRequestBlueprint> result) {
if (dataRead != ReadStatus.READ_FAILED) {
// update status only if bit switches
dataRead = ReadStatus.READ_FAILED;
updateStatus();
}
listeners.forEach(l -> {
l.handleError(result);
});
}
@Override
public void dispose() {
ModbusCommunicationInterface localComms = comms;
if (localComms != null) {
PollTask localInfoPoller = infoPoller;
if (localInfoPoller != null) {
localComms.unregisterRegularPoll(localInfoPoller);
}
PollTask localDataPoller = dataPoller;
if (localDataPoller != null) {
localComms.unregisterRegularPoll(localDataPoller);
}
}
// Comms will be close()'d by endpoint thing handler
comms = null;
}
private void updateStatus() {
logger.debug("Status update: Info {} Data {} ", infoRead, dataRead);
if (infoRead != ReadStatus.NOT_RECEIVED && dataRead != ReadStatus.NOT_RECEIVED) {
if (infoRead == dataRead) {
// both reads are ok or else both failed
if (infoRead == ReadStatus.READ_SUCCESS) {
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, INFO_DATA_READ_ERROR);
}
} else {
// either info or data read failed - update status with details
if (infoRead == ReadStatus.READ_FAILED) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, INFO_READ_ERROR);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, DATA_READ_ERROR);
}
}
} // else - one status isn't received yet - wait until both Modbus polls returns either success or error
}
@Override
public void childHandlerInitialized(ThingHandler childHandler, Thing childThing) {
listeners.add((E3DCWallboxThingHandler) childHandler);
}
@Override
public void childHandlerDisposed(ThingHandler childHandler, Thing childThing) {
listeners.remove(childHandler);
}
}

View File

@@ -0,0 +1,259 @@
/**
* Copyright (c) 2010-2020 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.e3dc.internal.handler;
import static org.openhab.binding.modbus.e3dc.internal.E3DCBindingConstants.*;
import static org.openhab.binding.modbus.e3dc.internal.modbus.E3DCModbusConstans.*;
import java.util.BitSet;
import java.util.Optional;
import java.util.OptionalInt;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.modbus.e3dc.internal.E3DCWallboxConfiguration;
import org.openhab.binding.modbus.e3dc.internal.dto.DataConverter;
import org.openhab.binding.modbus.e3dc.internal.dto.WallboxArray;
import org.openhab.binding.modbus.e3dc.internal.dto.WallboxBlock;
import org.openhab.binding.modbus.e3dc.internal.modbus.Data;
import org.openhab.binding.modbus.e3dc.internal.modbus.Data.DataType;
import org.openhab.binding.modbus.e3dc.internal.modbus.Parser;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.Bridge;
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.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.types.Command;
import org.openhab.io.transport.modbus.AsyncModbusFailure;
import org.openhab.io.transport.modbus.AsyncModbusReadResult;
import org.openhab.io.transport.modbus.ModbusCommunicationInterface;
import org.openhab.io.transport.modbus.ModbusReadRequestBlueprint;
import org.openhab.io.transport.modbus.ModbusRegisterArray;
import org.openhab.io.transport.modbus.ModbusWriteRegisterRequestBlueprint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link E3DCWallboxThingHandler} Basic modbus connection towards the E3DC device
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class E3DCWallboxThingHandler extends BaseThingHandler {
public enum ReadWriteSuccess {
NOT_RECEIVED,
SUCCESS,
FAILED
}
private static final String READ_WRITE_ERROR = "Modbus Data Read/Write Error";
private static final String READ_ERROR = "Modbus Read Error";
private static final String WRITE_ERROR = "Modbus Write Error";
ChannelUID wbAvailableChannel;
ChannelUID wbSunmodeChannel;
ChannelUID wbChargingAbortedChannel;
ChannelUID wbChargingChannel;
ChannelUID wbJackLockedChannel;
ChannelUID wbJackPluggedChannel;
ChannelUID wbSchukoOnChannel;
ChannelUID wbSchukoPluggedChannel;
ChannelUID wbSchukoLockedChannel;
ChannelUID wbSchukoRelay16Channel;
ChannelUID wbRelay16Channel;
ChannelUID wbRelay32Channel;
ChannelUID wb1phaseChannel;
private final Logger logger = LoggerFactory.getLogger(E3DCWallboxThingHandler.class);
private final Parser dataParser = new Parser(DataType.DATA);
private ReadWriteSuccess dataRead = ReadWriteSuccess.NOT_RECEIVED;
private ReadWriteSuccess dataWrite = ReadWriteSuccess.NOT_RECEIVED;
private volatile BitSet currentBitSet = new BitSet(16);
private @Nullable E3DCWallboxConfiguration config;
private @Nullable E3DCThingHandler bridgeHandler;
public E3DCWallboxThingHandler(Thing thing) {
super(thing);
wbAvailableChannel = new ChannelUID(thing.getUID(), WB_AVAILABLE_CHANNEL);
wbSunmodeChannel = new ChannelUID(thing.getUID(), WB_SUNMODE_CHANNEL);
wbChargingAbortedChannel = new ChannelUID(thing.getUID(), WB_CHARGING_ABORTED_CHANNEL);
wbChargingChannel = new ChannelUID(thing.getUID(), WB_CHARGING_CHANNEL);
wbJackLockedChannel = new ChannelUID(thing.getUID(), WB_JACK_LOCKED_CHANNEL);
wbJackPluggedChannel = new ChannelUID(thing.getUID(), WB_JACK_PLUGGED_CHANNEL);
wbSchukoOnChannel = new ChannelUID(thing.getUID(), WB_SCHUKO_ON_CHANNEL);
wbSchukoPluggedChannel = new ChannelUID(thing.getUID(), WB_SCHUKO_PLUGGED_CHANNEL);
wbSchukoLockedChannel = new ChannelUID(thing.getUID(), WB_SCHUKO_LOCKED_CHANNEL);
wbSchukoRelay16Channel = new ChannelUID(thing.getUID(), WB_SCHUKO_RELAY_16A_CHANNEL);
wbRelay16Channel = new ChannelUID(thing.getUID(), WB_RELAY_16A_CHANNEL);
wbRelay32Channel = new ChannelUID(thing.getUID(), WB_RELAY_32A_CHANNEL);
wb1phaseChannel = new ChannelUID(thing.getUID(), WB_1PHASE_CHANNEL);
}
@Override
public void initialize() {
updateStatus(ThingStatus.UNKNOWN);
config = getConfigAs(E3DCWallboxConfiguration.class);
Bridge bridge = getBridge();
if (bridge != null) {
ThingHandler handler = bridge.getHandler();
if (handler != null) {
bridgeHandler = ((E3DCThingHandler) handler);
} else {
logger.warn("Thing Handler null");
}
} else {
logger.warn("Bridge null");
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof OnOffType) {
int writeValue = 0;
synchronized (this) {
if (channelUID.getIdWithoutGroup().equals(WB_SUNMODE_CHANNEL)) {
currentBitSet.set(WB_SUNMODE_BIT, command.equals(OnOffType.ON));
} else if (channelUID.getIdWithoutGroup().equals(WB_CHARGING_ABORTED_CHANNEL)) {
currentBitSet.set(WB_CHARGING_ABORTED_BIT, command.equals(OnOffType.ON));
} else if (channelUID.getIdWithoutGroup().equals(WB_SCHUKO_ON_CHANNEL)) {
currentBitSet.set(WB_SCHUKO_ON_BIT, command.equals(OnOffType.ON));
} else if (channelUID.getIdWithoutGroup().equals(WB_1PHASE_CHANNEL)) {
currentBitSet.set(WB_1PHASE_BIT, command.equals(OnOffType.ON));
}
writeValue = DataConverter.toInt(currentBitSet);
logger.debug("Wallbox write {}", writeValue);
}
OptionalInt wallboxId = getWallboxId(config);
if (wallboxId.isPresent()) {
wallboxSet(wallboxId.getAsInt(), writeValue);
}
}
}
/**
* Wallbox Settings can be changed with one Integer
*
* @param wallboxId needed to calculate right register
* @param writeValue integer to be written
*/
public void wallboxSet(int wallboxId, int writeValue) {
E3DCThingHandler localBridgeHandler = bridgeHandler;
if (localBridgeHandler != null) {
ModbusCommunicationInterface comms = localBridgeHandler.getComms();
if (comms != null) {
ModbusRegisterArray regArray = new ModbusRegisterArray(writeValue);
ModbusWriteRegisterRequestBlueprint writeBluePrint = new ModbusWriteRegisterRequestBlueprint(
localBridgeHandler.getSlaveId(), WALLBOX_REG_START + wallboxId, regArray, false, 3);
comms.submitOneTimeWrite(writeBluePrint, result -> {
if (dataWrite != ReadWriteSuccess.SUCCESS) {
dataWrite = ReadWriteSuccess.SUCCESS;
updateStatus();
}
logger.debug("E3DC Modbus write response! {}", result.getResponse().toString());
}, failure -> {
if (dataWrite != ReadWriteSuccess.FAILED) {
dataWrite = ReadWriteSuccess.FAILED;
updateStatus();
}
logger.warn("E3DC Modbus write error! {}", failure.getRequest().toString());
});
}
}
}
private OptionalInt getWallboxId(@Nullable E3DCWallboxConfiguration c) {
if (c != null) {
return OptionalInt.of(c.wallboxId);
} else {
return OptionalInt.empty();
}
}
public void handle(AsyncModbusReadResult result) {
if (dataRead != ReadWriteSuccess.SUCCESS) {
dataRead = ReadWriteSuccess.SUCCESS;
updateStatus();
}
dataParser.handle(result);
Optional<Data> wbArrayOpt = dataParser.parse(DataType.WALLBOX);
if (wbArrayOpt.isPresent()) {
WallboxArray wbArray = (WallboxArray) wbArrayOpt.get();
OptionalInt wallboxId = getWallboxId(config);
if (wallboxId.isPresent()) {
Optional<WallboxBlock> blockOpt = wbArray.getWallboxBlock(wallboxId.getAsInt());
if (blockOpt.isPresent()) {
WallboxBlock block = blockOpt.get();
synchronized (this) {
currentBitSet = block.getBitSet();
}
updateState(wbAvailableChannel, block.wbAvailable);
updateState(wbSunmodeChannel, block.wbSunmode);
updateState(wbChargingAbortedChannel, block.wbChargingAborted);
updateState(wbChargingChannel, block.wbCharging);
updateState(wbJackLockedChannel, block.wbJackLocked);
updateState(wbJackPluggedChannel, block.wbJackPlugged);
updateState(wbSchukoOnChannel, block.wbSchukoOn);
updateState(wbSchukoPluggedChannel, block.wbSchukoPlugged);
updateState(wbSchukoLockedChannel, block.wbSchukoLocked);
updateState(wbSchukoRelay16Channel, block.wbSchukoRelay16);
updateState(wbRelay16Channel, block.wbRelay16);
updateState(wbRelay32Channel, block.wbRelay32);
updateState(wb1phaseChannel, block.wb1phase);
} else {
logger.debug("Unable to get ID {} from WallboxArray", wallboxId);
}
} else {
logger.debug("Wallbox ID {} not valid", wallboxId);
}
} else {
logger.debug("Unable to get {} from Bridge", DataType.WALLBOX);
}
}
public void handleError(AsyncModbusFailure<ModbusReadRequestBlueprint> result) {
if (dataRead != ReadWriteSuccess.FAILED) {
dataRead = ReadWriteSuccess.FAILED;
updateStatus();
}
}
private void updateStatus() {
if (dataWrite == ReadWriteSuccess.NOT_RECEIVED) {
// read success / write not happened yet => go online / offline
if (dataRead == ReadWriteSuccess.SUCCESS) {
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, READ_ERROR);
}
} else {
if (dataRead == dataWrite) {
// read and write same status - either go online or offline
if (dataRead == ReadWriteSuccess.SUCCESS) {
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, READ_WRITE_ERROR);
}
} else {
// either read or write failed - go offline with detailed status
if (dataRead == ReadWriteSuccess.SUCCESS) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, WRITE_ERROR);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, READ_ERROR);
}
}
}
}
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2010-2020 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.e3dc.internal.modbus;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link Data} Marker Interface for delivered Modbus Data
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public interface Data {
public enum DataType {
INFO,
POWER,
WALLBOX,
EMERGENCY,
STRINGS,
DATA // marks all types besides INFO
}
}

View File

@@ -0,0 +1,70 @@
/**
* Copyright (c) 2010-2020 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.e3dc.internal.modbus;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link E3DCModbusConstans} Variables for register handling.
* The numbers are taken from E3DC Modbus Spec Chapter 3.1 page 14 ff
* Registers start from 0 (not 1!) so from the documented registers subtract 1
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class E3DCModbusConstans {
// "String" registers at the beginning shall be read with very low frequency - 1 hour
public static final int INFO_POLL_REFRESH_TIME_MS = 60 * 60 * 1000;
// Constants where a certain Block starts and block size. Note: General offset is -1 so INFO_REG from E3DC Modbus
// Spec starts at 1 but it's Register 0!
public static final int INFO_REG_START = 0;
public static final int INFO_REG_SIZE = 67;
public static final int POWER_REG_START = 67;
public static final int POWER_REG_SIZE = 16;
public static final int EMS_REG_START = 83;
public static final int EMS_REG_SIZE = 2;
public static final int WALLBOX_REG_START = 87;
public static final int WALLBOX_REG_SIZE = 8;
public static final int STRINGS_REG_START = 95;
public static final int STRINGS_REG_SIZE = 9;
public static final int REGISTER_LENGTH = 104;
/*
* Some Registers are numbers but needs to be decoded into Bits
*/
// Wallbox Bit Definitions according to chapter 3.1.5 page 15
public static final int WB_AVAILABLE_BIT = 0;
public static final int WB_SUNMODE_BIT = 1;
public static final int WB_CHARGING_ABORTED_BIT = 2;
public static final int WB_CHARGING_BIT = 3;
public static final int WB_JACK_LOCKED_BIT = 4;
public static final int WB_JACK_PLUGGED_BIT = 5;
public static final int WB_SCHUKO_ON_BIT = 6;
public static final int WB_SCHUKO_PLUGGED_BIT = 7;
public static final int WB_SCHUKO_LOCKED_BIT = 8;
public static final int WB_SCHUKO_RELAY16A_BIT = 9;
public static final int WB_RELAY_16A_BIT = 10;
public static final int WB_RELAY_32A_BIT = 11;
public static final int WB_1PHASE_BIT = 12;
// EMS Bit Definitions according to chapter 3.1.3 page 17
public static final int EMS_CHARGING_LOCK_BIT = 0;
public static final int EMS_DISCHARGING_LOCK_BIT = 1;
public static final int EMS_AVAILABLE_BIT = 2;
public static final int EMS_WEATHER_CHARGING_BIT = 3;
public static final int EMS_REGULATION_BIT = 4;
public static final int EMS_CHARGE_LOCKTIME_BIT = 5;
public static final int EMS_DISCHARGE_LOCKTIME_BIT = 6;
}

View File

@@ -0,0 +1,129 @@
/**
* Copyright (c) 2010-2020 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.e3dc.internal.modbus;
import static org.openhab.binding.modbus.e3dc.internal.modbus.E3DCModbusConstans.*;
import java.util.Arrays;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.modbus.e3dc.internal.dto.EmergencyBlock;
import org.openhab.binding.modbus.e3dc.internal.dto.InfoBlock;
import org.openhab.binding.modbus.e3dc.internal.dto.PowerBlock;
import org.openhab.binding.modbus.e3dc.internal.dto.StringBlock;
import org.openhab.binding.modbus.e3dc.internal.dto.WallboxArray;
import org.openhab.binding.modbus.e3dc.internal.modbus.Data.DataType;
import org.openhab.io.transport.modbus.AsyncModbusReadResult;
import org.openhab.io.transport.modbus.ModbusRegister;
import org.openhab.io.transport.modbus.ModbusRegisterArray;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link InfoBlockCallback} class receives callbacks from modbus poller
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class Parser {
private static final int MEASURE_COUNT = 100;
private final Logger logger = LoggerFactory.getLogger(Parser.class);
private DataType callbackType;
private byte[] bArray;
private int size;
private int counter = 0;
private long maxDuration = Long.MIN_VALUE;
private long minDuration = Long.MAX_VALUE;
private long avgDuration = 0;
public Parser(DataType type) {
callbackType = type;
if (type.equals(DataType.INFO)) {
size = INFO_REG_SIZE * 2;
bArray = new byte[size];
} else {
size = (REGISTER_LENGTH - INFO_REG_SIZE) * 2;
bArray = new byte[size];
}
}
public void handle(AsyncModbusReadResult result) {
byte[] newArray = new byte[size];
long startTime = System.currentTimeMillis();
Optional<ModbusRegisterArray> opt = result.getRegisters();
if (opt.isPresent()) {
ModbusRegisterArray registers = opt.get();
int i = 0;
for (ModbusRegister reg : registers) {
System.arraycopy(reg.getBytes(), 0, newArray, i, 2);
i += 2;
}
setArray(newArray);
long duration = System.currentTimeMillis() - startTime;
avgDuration += duration;
minDuration = Math.min(minDuration, duration);
maxDuration = Math.max(maxDuration, duration);
counter++;
if (counter % MEASURE_COUNT == 0) {
logger.debug("Min {} Max {} Avg {}", minDuration, maxDuration, avgDuration / MEASURE_COUNT);
avgDuration = 0;
minDuration = Long.MAX_VALUE;
maxDuration = Long.MIN_VALUE;
}
} else {
logger.warn("Modbus read result doesn't return expected registers");
}
}
public synchronized void setArray(byte[] b) {
if (b.length != size) {
logger.warn("Wrong byte size received. Should be {} but is {}. Data maybe corrupted!", size, b.length);
}
bArray = b.clone();
}
public Optional<Data> parse(DataType type) {
synchronized (bArray) {
if (type.equals(DataType.INFO) && callbackType.equals(DataType.INFO)) {
return Optional.of(new InfoBlock(Arrays.copyOfRange(bArray, INFO_REG_START, INFO_REG_SIZE * 2)));
} else if (type.equals(DataType.POWER) && callbackType.equals(DataType.DATA)) {
int start = (POWER_REG_START - INFO_REG_SIZE) * 2;
int end = start + POWER_REG_SIZE * 2;
return Optional.of(new PowerBlock(Arrays.copyOfRange(bArray, start, end)));
} else if (type.equals(DataType.EMERGENCY) && callbackType.equals(DataType.DATA)) {
int start = (EMS_REG_START - INFO_REG_SIZE) * 2;
int end = start + EMS_REG_SIZE * 2;
return Optional.of(new EmergencyBlock(Arrays.copyOfRange(bArray, start, end)));
} else if (type.equals(DataType.WALLBOX) && callbackType.equals(DataType.DATA)) {
int start = (WALLBOX_REG_START - INFO_REG_SIZE) * 2;
int end = start + WALLBOX_REG_SIZE * 2;
return Optional.of(new WallboxArray(Arrays.copyOfRange(bArray, start, end)));
} else if (type.equals(DataType.STRINGS) && callbackType.equals(DataType.DATA)) {
int start = (STRINGS_REG_START - INFO_REG_SIZE) * 2;
int end = start + STRINGS_REG_SIZE * 2;
return Optional.of(new StringBlock(Arrays.copyOfRange(bArray, start, end)));
}
}
logger.warn("Wrong Block requested. Request is {} but type is {}", type, callbackType);
return Optional.empty();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(this.getClass().getName()).append(":").append(callbackType);
return sb.toString();
}
}

View File

@@ -0,0 +1,81 @@
# thing types
thing-type.modbus.e3dc.label = E3DC Hauskraftwerk
thing-type.modbus.e3dc.description = Zeigt die Leistungswerte, String Details, Notstrom Zustand und generelle Details zu Ihrem E3DC Hauskraftwerk
thing-type.modbus.e3dc-wallbox.label = E3DC Wallbox
thing-type.modbus.e3dc-wallbox.description = Zeigt die Einstellungen der verbundenen Wallbox. Schalter wie "Sonnenmodus" oder "1-phasiges Laden" können geändert werden
# thing type config description
thing-type.config.modbus.e3dc.refresh.label = Zyklische Abfragezeit
thing-type.config.modbus.e3dc.refresh.description = Intervall in Millisekunden, wie oft die Werte abgerufen werden sollen
thing-type.config.modbus.e3dc-wallbox.wallboxId.label = Wallbox-Nummer
thing-type.config.modbus.e3dc-wallbox.wallboxId.description = Das E3DC Gerät unterstützt bis zu 8 Wallboxen - Wählen Sie einen Wert zwischen 0 und 7
# Channel group types
channel-group-type.modbus.info-values.label = Informationen
channel-group-type.modbus.info-values.description = Basisinformationen zum E3DC Hauskraftwerk wie Modellname, Seriennummer und Software-Versionen
channel-group-type.modbus.power-values.label = Leistungswerte
channel-group-type.modbus.power-values.description = Elektrische Werte der angeschlossenen Erzeuger (Photovoltaik, Batterie, ...) und Verbraucher (Haushaltsverbrauch, Wallbox,...)
channel-group-type.modbus.string-values.label = String Details
channel-group-type.modbus.string-values.description = Detaillierte Werte der angeschlossenen Photovoltaik Strings. Zeigt die einzelnen Werte jedes angeschlossenen Strings
channel-group-type.modbus.emergency-values.label = Notstrom Einstellungen
channel-group-type.modbus.emergency-values.description = Status und Werte der Notstromversorgung wie Batterie Lade- und Entladesperren, Einspeise-Abregelung
# Wallbox Channels
channel-type.modbus.wb-available-channel.label = Vorhanden
channel-type.modbus.wb-sunmode-channel.label = Sonnenmodus
channel-type.modbus.wb-charging-aborted-channel.label = Laden Abgebrochen
channel-type.modbus.wb-charging-channel.label = Auto lädt
channel-type.modbus.wb-jack-locked-channel.label = Ladekabel Verriegelt
channel-type.modbus.wb-jack-plugged-channel.label = Ladekabel Gesteckt
channel-type.modbus.wb-schuko-on-channel.label = Schuko-Steckdose An
channel-type.modbus.wb-schuko-plugged-channel.label = Schuko-Stecker Gesteckt
channel-type.modbus.wb-schuko-locked-channel.label = Schuko-Stecker Verriegelt
channel-type.modbus.wb-schuko-relay-16a-channel.label = Schuko-Steckdose 16A Relais An
channel-type.modbus.wb-relay-16a-channel.label = 16A Relais An
channel-type.modbus.wb-relay-32a-channel.label = 32A Relais An
channel-type.modbus.wb-1phase-channel.label = 1-phasiges Laden An
# Emergency Channels
channel-type.modbus.emergency-power-status-channel.label = Notstrom Status
channel-type.modbus.battery-charging-lock-channel.label = Batterie Laden Gesperrt
channel-type.modbus.battery-discharging-lock-channel.label = Batterie Entladen Gesperrt
channel-type.modbus.emergency-power-possible-channel.label = Notstrom Versorgung Möglich
channel-type.modbus.weather-predicted-charging-channel.label = Wetterbasiertes Laden
channel-type.modbus.regulation-status-channel.label = Einspeise Abregelung
channel-type.modbus.charge-lock-time-channel.label = Batterie Ladesperrzeiten Aktiv
channel-type.modbus.discharge-lock-time-channel.label = Batterie Entladesperrzeiten Aktiv
# Info Channels
channel-type.modbus.modbus-id-channel.label = Modbus ID
channel-type.modbus.modbus-firmware-channel.label = Modbus Firmware
channel-type.modbus.supported-registers-channel.label = Unterstützte Register
channel-type.modbus.manufacturer-name-channel.label = Hersteller
channel-type.modbus.model-name-channel.label = Modellname
channel-type.modbus.serial-number-channel.label = Seriennummer
channel-type.modbus.firmware-release-channel.label = Firmware
# Power Channels
channel-type.modbus.pv-power-supply-channel.label = Photovoltaik Leistung
channel-type.modbus.battery-power-supply-channel.label = Batterie Entladen
channel-type.modbus.battery-power-consumption-channel.label = Batterie Laden
channel-type.modbus.household-power-consumption-channel.label = Haushalt Verbrauch
channel-type.modbus.grid-power-consumption-channel.label = Stromnetz Verbrauch
channel-type.modbus.grid-power-supply-channel.label = Stromnetz Einspeisung
channel-type.modbus.external-power-supply-channel.label = Externer Stromproduzent
channel-type.modbus.wallbox-power-consumption-channel.label = Wallbox Ladestrom
channel-type.modbus.wallbox-pv-power-consumption-channel.label = Wallbox Photovoltaik Ladestrom
channel-type.modbus.autarky-channel.label = Autarkie in Prozent
channel-type.modbus.self-consumption-channel.label = Eigenverbrauch in Prozent
channel-type.modbus.battery-soc-channel.label = Batterie Ladestand in Prozent
# String Channels
channel-type.modbus.string1-dc-voltage-channel.label = String 1 Spannung
channel-type.modbus.string2-dc-voltage-channel.label = String 2 Spannung
channel-type.modbus.string3-dc-voltage-channel.label = String 3 Spannung
channel-type.modbus.string1-dc-current-channel.label = String 1 Stromstärke
channel-type.modbus.string2-dc-current-channel.label = String 2 Stromstärke
channel-type.modbus.string3-dc-current-channel.label = String 3 Stromstärke
channel-type.modbus.string1-dc-output-channel.label = String 1 Leistung
channel-type.modbus.string2-dc-output-channel.label = String 2 Leistung
channel-type.modbus.string3-dc-output-channel.label = String 3 Leistung

View File

@@ -0,0 +1,27 @@
<?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">
<bridge-type id="e3dc">
<supported-bridge-type-refs>
<bridge-type-ref id="tcp"/>
</supported-bridge-type-refs>
<label>E3DC Home Power Plant</label>
<description>Provide Power values, String Details, Emergency Power Status and general Information of your E3DC Home
Power Plant</description>
<channel-groups>
<channel-group id="info" typeId="info-values"/>
<channel-group id="power" typeId="power-values"/>
<channel-group id="strings" typeId="string-values"/>
<channel-group id="emergency" typeId="emergency-values"/>
</channel-groups>
<config-description>
<parameter name="refresh" type="integer" min="1000" unit="ms">
<label>Refresh Interval</label>
<description>Refresh Rate of E3DC values in Milliseconds</description>
<default>2000</default>
</parameter>
</config-description>
</bridge-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,21 @@
<?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">
<channel-group-type id="emergency-values">
<label>EMS Settings</label>
<description>Provides values of Emergency Power Status (EMS) and regulations like Battery charging / discharging
restrictions</description>
<channels>
<channel id="emergency-power-status" typeId="emergency-power-status-channel"/>
<channel id="battery-charging-lock" typeId="battery-charging-lock-channel"/>
<channel id="battery-discharging-lock" typeId="battery-discharging-lock-channel"/>
<channel id="emergency-power-possible" typeId="emergency-power-possible-channel"/>
<channel id="weather-predicted-charging" typeId="weather-predicted-charging-channel"/>
<channel id="regulation-status" typeId="regulation-status-channel"/>
<channel id="charge-lock-time" typeId="charge-lock-time-channel"/>
<channel id="discharge-lock-time" typeId="discharge-lock-time-channel"/>
</channels>
</channel-group-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,46 @@
<?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">
<channel-type id="emergency-power-status-channel">
<item-type>String</item-type>
<label>Emergency Power Status</label>
<description>Indicates if Emergency Power Supply is possible or not, active or inactive</description>
</channel-type>
<channel-type id="battery-charging-lock-channel">
<item-type>Switch</item-type>
<label>Battery Charge Locked</label>
<description>Battery charging is locked</description>
</channel-type>
<channel-type id="battery-discharging-lock-channel">
<item-type>Switch</item-type>
<label>Battery Discharge Locked</label>
<description>Battery discharging is locked</description>
</channel-type>
<channel-type id="emergency-power-possible-channel">
<item-type>Switch</item-type>
<label>Emergency Power Possible</label>
<description>Emergency Power Supply is possible</description>
</channel-type>
<channel-type id="weather-predicted-charging-channel">
<item-type>Switch</item-type>
<label>Weather Predicted Battery Charging</label>
<description>Weather Predicted Battery Charging is activated</description>
</channel-type>
<channel-type id="regulation-status-channel">
<item-type>Switch</item-type>
<label>Grid Power Supply Regulation</label>
<description>Grid Power Supply is currently regulated</description>
</channel-type>
<channel-type id="charge-lock-time-channel">
<item-type>Switch</item-type>
<label>Charge Lock Time Active</label>
<description>Charge Lock Time is currently active</description>
</channel-type>
<channel-type id="discharge-lock-time-channel">
<item-type>Switch</item-type>
<label>Discharge Lock Time Active</label>
<description>Discharge Lock Time is currently active</description>
</channel-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,19 @@
<?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">
<channel-group-type id="info-values">
<label>Information</label>
<description>Basic Information of your E3DC Device like Model Name, Serial Number and Software Versions</description>
<channels>
<channel id="modbus-id" typeId="modbus-id-channel"/>
<channel id="modbus-firmware" typeId="modbus-firmware-channel"/>
<channel id="supported-registers" typeId="supported-registers-channel"/>
<channel id="manufacturer-name" typeId="manufacturer-name-channel"/>
<channel id="model-name" typeId="model-name-channel"/>
<channel id="serial-number" typeId="serial-number-channel"/>
<channel id="firmware-release" typeId="firmware-release-channel"/>
</channels>
</channel-group-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,41 @@
<?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">
<channel-type id="modbus-id-channel">
<item-type>String</item-type>
<label>Modbus-ID</label>
<description>Modbus ID / Magic Byte of E3DC</description>
</channel-type>
<channel-type id="modbus-firmware-channel">
<item-type>String</item-type>
<label>Modbus Firmware</label>
<description>Version of Modbus Firmware</description>
</channel-type>
<channel-type id="supported-registers-channel">
<item-type>Number</item-type>
<label>Supported Registers</label>
<description>Number of registers supported by Modbus</description>
</channel-type>
<channel-type id="manufacturer-name-channel">
<item-type>String</item-type>
<label>Manufacturer Name</label>
<description>Name of the Device Manufacturer</description>
</channel-type>
<channel-type id="model-name-channel">
<item-type>String</item-type>
<label>Model Name</label>
<description>Name of the E3DC Model</description>
</channel-type>
<channel-type id="firmware-release-channel">
<item-type>String</item-type>
<label>Firmware Release</label>
<description>Firmware installed on this particular E3DC Device</description>
</channel-type>
<channel-type id="serial-number-channel">
<item-type>String</item-type>
<label>Serial Number</label>
<description>Serial Number of this particular E3DC Device</description>
</channel-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,25 @@
<?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">
<channel-group-type id="power-values">
<label>Power Values</label>
<description>Provides values of your attached electrical Producers (Photovoltaic, Battery, ... and Consumers
(Household, Wallbox, ...)</description>
<channels>
<channel id="pv-power-supply" typeId="pv-power-supply-channel"/>
<channel id="battery-power-supply" typeId="battery-power-supply-channel"/>
<channel id="battery-power-consumption" typeId="battery-power-consumption-channel"/>
<channel id="household-power-consumption" typeId="household-power-consumption-channel"/>
<channel id="grid-power-consumption" typeId="grid-power-consumption-channel"/>
<channel id="grid-power-supply" typeId="grid-power-supply-channel"/>
<channel id="external-power-supply" typeId="external-power-supply-channel"/>
<channel id="wallbox-power-consumption" typeId="wallbox-power-consumption-channel"/>
<channel id="wallbox-pv-power-consumption" typeId="wallbox-pv-power-consumption-channel"/>
<channel id="autarky" typeId="autarky-channel"/>
<channel id="self-consumption" typeId="self-consumption-channel"/>
<channel id="battery-soc" typeId="battery-soc-channel"/>
</channels>
</channel-group-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,66 @@
<?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">
<channel-type id="pv-power-supply-channel">
<item-type>Number:Power</item-type>
<label>PV Output</label>
<description>Photovoltaic Power Production</description>
</channel-type>
<channel-type id="battery-power-supply-channel">
<item-type>Number:Power</item-type>
<label>Battery Discharge</label>
<description>Battery discharges and provides Power</description>
</channel-type>
<channel-type id="battery-power-consumption-channel">
<item-type>Number:Power</item-type>
<label>Battery Charge</label>
<description>Battery charges and consumes Power</description>
</channel-type>
<channel-type id="household-power-consumption-channel">
<item-type>Number:Power</item-type>
<label>Household Consumption</label>
<description>Household consuming Power</description>
</channel-type>
<channel-type id="grid-power-consumption-channel">
<item-type>Number:Power</item-type>
<label>Grid Power Consumption</label>
<description>Grid Power is needed in order to satisfy your overall Power consumption</description>
</channel-type>
<channel-type id="grid-power-supply-channel">
<item-type>Number:Power</item-type>
<label>Grid Power Supply</label>
<description>More Photovoltaic Power is produced than needed. Additional Power is provided towards the Grid</description>
</channel-type>
<channel-type id="external-power-supply-channel">
<item-type>Number:Power</item-type>
<label>External Power Supply</label>
<description>Power produced by an external device which is attached to your E3DC device</description>
</channel-type>
<channel-type id="wallbox-power-consumption-channel">
<item-type>Number:Power</item-type>
<label>Wallbox Power Consumption</label>
<description>Power consumption of attached Wallboxes</description>
</channel-type>
<channel-type id="wallbox-pv-power-consumption-channel">
<item-type>Number:Power</item-type>
<label>Wallbox PV Power Consumption</label>
<description>Photovoltaic Power consumption (PV plus Battery) of attached Wallboxes</description>
</channel-type>
<channel-type id="autarky-channel">
<item-type>Number:Dimensionless</item-type>
<label>Autarky</label>
<description>Your current Autarky Level in Percent</description>
</channel-type>
<channel-type id="self-consumption-channel">
<item-type>Number:Dimensionless</item-type>
<label>Self Consumtion</label>
<description>Your current Photovoltaic Self Consumption Level in Percent</description>
</channel-type>
<channel-type id="battery-soc-channel">
<item-type>Number:Dimensionless</item-type>
<label>Battery State Of Charge</label>
<description>Charge Level of your attached Battery in Percent</description>
</channel-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,22 @@
<?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">
<channel-group-type id="string-values">
<label>String Details</label>
<description>Provide detailed values of your attached Photovoltaic Strings. Evaluate how much Power each String
provides</description>
<channels>
<channel id="string1-dc-voltage" typeId="string1-dc-voltage-channel"/>
<channel id="string2-dc-voltage" typeId="string2-dc-voltage-channel"/>
<channel id="string3-dc-voltage" typeId="string3-dc-voltage-channel"/>
<channel id="string1-dc-current" typeId="string1-dc-current-channel"/>
<channel id="string2-dc-current" typeId="string2-dc-current-channel"/>
<channel id="string3-dc-current" typeId="string3-dc-current-channel"/>
<channel id="string1-dc-output" typeId="string1-dc-output-channel"/>
<channel id="string2-dc-output" typeId="string2-dc-output-channel"/>
<channel id="string3-dc-output" typeId="string3-dc-output-channel"/>
</channels>
</channel-group-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,51 @@
<?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">
<channel-type id="string1-dc-voltage-channel">
<item-type>Number:ElectricPotential</item-type>
<label>String 1 Potential</label>
<description>Voltage on String 1</description>
</channel-type>
<channel-type id="string2-dc-voltage-channel">
<item-type>Number:ElectricPotential</item-type>
<label>String 2 Potential</label>
<description>Voltage on String 2</description>
</channel-type>
<channel-type id="string3-dc-voltage-channel">
<item-type>Number:ElectricPotential</item-type>
<label>String 3 Potential</label>
<description>Voltage on String 3</description>
</channel-type>
<channel-type id="string1-dc-current-channel">
<item-type>Number:ElectricCurrent</item-type>
<label>String 1 Current</label>
<description>Current on String 1</description>
</channel-type>
<channel-type id="string2-dc-current-channel">
<item-type>Number:ElectricCurrent</item-type>
<label>String 2 Current</label>
<description>Current on String 2</description>
</channel-type>
<channel-type id="string3-dc-current-channel">
<item-type>Number:ElectricCurrent</item-type>
<label>String 3 Current</label>
<description>Current on String 3</description>
</channel-type>
<channel-type id="string1-dc-output-channel">
<item-type>Number:Power</item-type>
<label>String 1 Power</label>
<description>Power produced by String 1</description>
</channel-type>
<channel-type id="string2-dc-output-channel">
<item-type>Number:Power</item-type>
<label>String 2 Power</label>
<description>Power produced by String 2</description>
</channel-type>
<channel-type id="string3-dc-output-channel">
<item-type>Number:Power</item-type>
<label>String 3 Power</label>
<description>Power produced by String 3</description>
</channel-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,35 @@
<?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="e3dc-wallbox">
<supported-bridge-type-refs>
<bridge-type-ref id="e3dc"/>
</supported-bridge-type-refs>
<label>E3DC Wallbox</label>
<description>Provide your Wallbox Settings. Switches like "Sunmode" or "1-Phase Charging" can be changed</description>
<channels>
<channel id="wb-available" typeId="wb-available-channel"/>
<channel id="wb-sunmode" typeId="wb-sunmode-channel"/>
<channel id="wb-charging-aborted" typeId="wb-charging-aborted-channel"/>
<channel id="wb-charging" typeId="wb-charging-channel"/>
<channel id="wb-jack-locked" typeId="wb-jack-locked-channel"/>
<channel id="wb-jack-plugged" typeId="wb-jack-plugged-channel"/>
<channel id="wb-schuko-on" typeId="wb-schuko-on-channel"/>
<channel id="wb-schuko-plugged" typeId="wb-schuko-plugged-channel"/>
<channel id="wb-schuko-locked" typeId="wb-schuko-locked-channel"/>
<channel id="wb-schuko-relay-16a" typeId="wb-schuko-relay-16a-channel"/>
<channel id="wb-relay-16a" typeId="wb-relay-16a-channel"/>
<channel id="wb-relay-32a" typeId="wb-relay-32a-channel"/>
<channel id="wb-1phase" typeId="wb-1phase-channel"/>
</channels>
<config-description>
<parameter name="wallboxId" type="integer" min="0" max="7" required="true">
<label>Wallbox ID</label>
<description>E3DC supports up to 8 Wallboxes - select a value from 0 to 7</description>
<default>0</default>
</parameter>
</config-description>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,71 @@
<?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">
<channel-type id="wb-available-channel">
<item-type>Switch</item-type>
<label>Available</label>
<description>Indicates if the Wallbox is attached. Check your Wallbox ID in offline case</description>
</channel-type>
<channel-type id="wb-sunmode-channel">
<item-type>Switch</item-type>
<label>Sun Mode</label>
<description>Activate / Deactivate Sun Mode. Off case takes Grid Power to ensure highest possible charging</description>
</channel-type>
<channel-type id="wb-charging-aborted-channel">
<item-type>Switch</item-type>
<label>Charging Aborted</label>
<description>Wallbox charging is aborted</description>
</channel-type>
<channel-type id="wb-charging-channel">
<item-type>Switch</item-type>
<label>Charging</label>
<description>Wallbox is charging</description>
</channel-type>
<channel-type id="wb-jack-locked-channel">
<item-type>Switch</item-type>
<label>Jack Locked</label>
<description>Jack is locked</description>
</channel-type>
<channel-type id="wb-jack-plugged-channel">
<item-type>Switch</item-type>
<label>Jack Plugged</label>
<description>Jack is plugged</description>
</channel-type>
<channel-type id="wb-schuko-on-channel">
<item-type>Switch</item-type>
<label>Schuko Socket On</label>
<description>If your Wallbox has an additional Schuko Socket it provides state ON or OFF</description>
</channel-type>
<channel-type id="wb-schuko-plugged-channel">
<item-type>Switch</item-type>
<label>Schuko Socket Plugged</label>
<description>If your Wallbox has an additional Schuko Socket it provides plugged state ON or OFF</description>
</channel-type>
<channel-type id="wb-schuko-locked-channel">
<item-type>Switch</item-type>
<label>Schuko Socket Locked</label>
<description>If your Wallbox has an additional Schuko Socket it provides locked state ON or OFF</description>
</channel-type>
<channel-type id="wb-schuko-relay-16a-channel">
<item-type>Switch</item-type>
<label>Schuko 16A Relay On</label>
<description>Schuko 16A Relay is ON</description>
</channel-type>
<channel-type id="wb-relay-16a-channel">
<item-type>Switch</item-type>
<label>16A Relay On</label>
<description>Wallbox 16A Relay is ON</description>
</channel-type>
<channel-type id="wb-relay-32a-channel">
<item-type>Switch</item-type>
<label>32A Relay On</label>
<description>Wallbox 32A Relay is ON</description>
</channel-type>
<channel-type id="wb-1phase-channel">
<item-type>Switch</item-type>
<label>1-Phase Charging</label>
<description>1-phase charging is activated. If OFF 3-phase charging is activated</description>
</channel-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,121 @@
/**
* Copyright (c) 2010-2020 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.e3dc.dto;
import static org.junit.Assert.*;
import java.util.Optional;
import org.junit.Before;
import org.junit.Test;
import org.openhab.binding.modbus.e3dc.internal.dto.EmergencyBlock;
import org.openhab.binding.modbus.e3dc.internal.dto.PowerBlock;
import org.openhab.binding.modbus.e3dc.internal.dto.StringBlock;
import org.openhab.binding.modbus.e3dc.internal.dto.WallboxArray;
import org.openhab.binding.modbus.e3dc.internal.dto.WallboxBlock;
import org.openhab.binding.modbus.e3dc.internal.modbus.Data;
import org.openhab.binding.modbus.e3dc.internal.modbus.Data.DataType;
import org.openhab.binding.modbus.e3dc.internal.modbus.Parser;
import org.openhab.core.library.types.OnOffType;
/**
* The {@link DataBlockTest} Test Data Transfer Objects of frequent Data Block
*
* @author Bernd Weymann - Initial contribution
*/
public class DataBlockTest {
private Parser mc;
@Before
public void setup() {
byte[] dataBlock = new byte[] { 0, -14, 0, 0, -2, -47, -1, -1, 2, 47, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 99, 99, 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
125, 2, 21, 0, 0, 0, 27, 0, 26, 0, 0, 0, 103, 0, -117, 0, 0 };
mc = new Parser(DataType.DATA);
mc.setArray(dataBlock);
}
@Test
public void testValidPowerBlock() {
Optional<Data> dataOpt = mc.parse(DataType.POWER);
assertTrue(dataOpt.isPresent());
PowerBlock b = (PowerBlock) dataOpt.get();
assertEquals("PV Supply", "242.0 W", b.pvPowerSupply.toString());
assertEquals("Grid Supply", "14.0 W", b.gridPowerSupply.toString());
assertEquals("Grid Consumption", "0.0 W", b.gridPowerConsumpition.toString());
assertEquals("Battery Supply", "303.0 W", b.batteryPowerSupply.toString());
}
@Test
public void testValidWallboxBlock() {
Optional<Data> wba = mc.parse(DataType.WALLBOX);
assertTrue(wba.isPresent());
WallboxArray a = (WallboxArray) wba.get();
assertNotNull(a);
Optional<WallboxBlock> o = a.getWallboxBlock(0);
WallboxBlock b = o.get();
assertNotNull(b);
assertEquals("Wallbox available", OnOffType.ON, b.wbAvailable);
assertEquals("Wallbox Sunmode", OnOffType.ON, b.wbSunmode);
assertEquals("Wallbox 1phase", OnOffType.OFF, b.wb1phase);
assertEquals("Wallbox charging", OnOffType.OFF, b.wbCharging);
}
@Test
public void testValidEmergency() {
Optional<Data> dataOpt = mc.parse(DataType.EMERGENCY);
assertTrue(dataOpt.isPresent());
EmergencyBlock b = (EmergencyBlock) dataOpt.get();
assertEquals("EMS Status", EmergencyBlock.EP_NOT_SUPPORTED, b.epStatus.toFullString());
assertEquals("Battery charging locked", OnOffType.OFF, b.batteryChargingLocked);
assertEquals("Battery discharging locked", OnOffType.OFF, b.batteryDischargingLocked);
assertEquals("EP possible", OnOffType.OFF, b.epPossible);
assertEquals("Weather Predicted charging", OnOffType.OFF, b.weatherPredictedCharging);
assertEquals("Regulation Status", OnOffType.OFF, b.regulationStatus);
assertEquals("Charge Lock Time", OnOffType.OFF, b.chargeLockTime);
assertEquals("Discharge Lock Time", OnOffType.OFF, b.dischargeLockTime);
}
@Test
public void testValidStringDetailsStringBlock() {
Optional<Data> dataOpt = mc.parse(DataType.STRINGS);
assertTrue(dataOpt.isPresent());
StringBlock b = (StringBlock) dataOpt.get();
assertEquals("String 1 V", 381, b.string1Volt.intValue());
assertEquals("String 1 V", "V", b.string1Volt.getUnit().toString());
assertEquals("String 2 V", 533, b.string2Volt.intValue());
assertEquals("String 1 V", "V", b.string2Volt.getUnit().toString());
assertEquals("String 3 V", 0, b.string3Volt.intValue());
assertEquals("String 1 V", "V", b.string3Volt.getUnit().toString());
assertEquals("String 1 A", 0.27, b.string1Ampere.doubleValue(), 0.01);
assertEquals("String 1 A", "A", b.string1Ampere.getUnit().toString());
assertEquals("String 2 A", 0.26, b.string2Ampere.doubleValue(), 0.01);
assertEquals("String 2 A", "A", b.string2Ampere.getUnit().toString());
assertEquals("String 3 A", 0, b.string3Ampere.doubleValue(), 0.01);
assertEquals("String 3 A", "A", b.string3Ampere.getUnit().toString());
assertEquals("String 1 W", 103, b.string1Watt.intValue());
assertEquals("String 1 W", "W", b.string1Watt.getUnit().toString());
assertEquals("String 2 W", 139, b.string2Watt.intValue());
assertEquals("String 2 W", "W", b.string2Watt.getUnit().toString());
assertEquals("String 3 W", 0, b.string3Watt.intValue());
assertEquals("String 3 W", "W", b.string3Watt.getUnit().toString());
}
@Test
public void testInvalidInfoblock() {
Optional<Data> infoOpt = mc.parse(DataType.INFO);
assertFalse(infoOpt.isPresent());
}
}

View File

@@ -0,0 +1,77 @@
/**
* Copyright (c) 2010-2020 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.e3dc.dto;
import static org.junit.Assert.*;
import java.util.Optional;
import org.junit.Before;
import org.junit.Test;
import org.openhab.binding.modbus.e3dc.internal.dto.InfoBlock;
import org.openhab.binding.modbus.e3dc.internal.modbus.Data;
import org.openhab.binding.modbus.e3dc.internal.modbus.Data.DataType;
import org.openhab.binding.modbus.e3dc.internal.modbus.Parser;
/**
* The {@link InfoTest} Test Data Transfer Objects of Information Block
*
* @author Bernd Weymann - Initial contribution
*/
public class InfoTest {
private Parser mc;
@Before
public void setup() {
byte[] infoBlock = new byte[] { -29, -36, 1, 2, 0, -120, 69, 51, 47, 68, 67, 32, 71, 109, 98, 72, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 49, 48, 32, 69, 32, 65, 73, 79, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 78, 73, 78, 73, 84, 73, 65, 76, 73, 90, 69, 68,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 49, 48, 95, 50, 48, 50, 48, 95, 48, 52, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
mc = new Parser(DataType.INFO);
mc.setArray(infoBlock);
}
@Test
public void testvalidInformationBlock() {
Optional<Data> infoOpt = mc.parse(DataType.INFO);
assertTrue(infoOpt.isPresent());
InfoBlock b = (InfoBlock) infoOpt.get();
assertNotNull(b);
assertEquals("MagicByte", "E3DC", b.modbusId.toString());
assertEquals("Model", "S10 E AIO", b.modelName.toString());
assertEquals("Firmware", "S10_2020_04", b.firmware.toString());
assertEquals("Manufacturer", "E3/DC GmbH", b.manufacturer.toString());
}
@Test
public void testWrongQuery() {
Optional<Data> dataOpt = mc.parse(DataType.POWER);
assertFalse(dataOpt.isPresent());
}
@Test
public void testInvalidBlockSize() {
byte[] infoBlock = new byte[] { -29, -36, 1, 2, 0, -120, 69, 51, 47, 68, 67, 32, 71, 109, 98, 72, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 49, 48, 32, 69, 32, 65, 73, 79, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 78, 73, 78, 73, 84, 73, 65, 76, 73, 90, 69, 68,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 49, 48, 95, 50, 48, 50, 48, 95, 48 };
Parser mc = new Parser(DataType.INFO);
mc.setArray(infoBlock);
Optional<Data> infoOpt = mc.parse(DataType.INFO);
InfoBlock b = (InfoBlock) infoOpt.get();
// block still valid but data maybe corrupted => logged in warnings
assertTrue(infoOpt.isPresent());
assertNotNull(b);
}
}

View File

@@ -0,0 +1,121 @@
/**
* Copyright (c) 2010-2020 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.e3dc.internal.handler;
import static org.mockito.Mockito.*;
import java.nio.ByteBuffer;
import java.util.HashMap;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Test;
import org.mockito.ArgumentMatchers;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandlerCallback;
import org.openhab.io.transport.modbus.AsyncModbusFailure;
import org.openhab.io.transport.modbus.AsyncModbusReadResult;
import org.openhab.io.transport.modbus.ModbusReadRequestBlueprint;
import org.openhab.io.transport.modbus.ModbusRegister;
import org.openhab.io.transport.modbus.ModbusRegisterArray;
/**
* The {@link E3DCHandlerStateTest} Test State handling of Handler if different results occurs
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class E3DCHandlerStateTest {
ThingStatusInfo unknownStatus = new ThingStatusInfo(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, null);
ThingStatusInfo onlineStatus = new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, null);
ThingStatusInfo offlineStatus = new ThingStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
E3DCThingHandler.DATA_READ_ERROR);
@Test
public void testStatusChain() {
Bridge bridge = mock(Bridge.class);
ThingUID uid = new ThingUID("modbus", "e3dc", "powerplant");
when(bridge.getUID()).thenReturn(uid);
ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
E3DCThingHandler handler = new E3DCThingHandler(bridge);
handler.setCallback(callback);
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("refresh", 2000);
Configuration config = new Configuration(map);
when(bridge.getConfiguration()).thenReturn(config);
handler.initialize();
verify(callback).statusUpdated(ArgumentMatchers.eq((Thing) bridge), ArgumentMatchers.eq(unknownStatus));
// Initializing is ongoing - now simulate info and data callback
handler.handleInfoResult(getInfoResult());
verify(callback).statusUpdated(ArgumentMatchers.eq((Thing) bridge), ArgumentMatchers.eq(unknownStatus));
handler.handleDataResult(getDataResult());
verify(callback, times(1)).statusUpdated(ArgumentMatchers.eq((Thing) bridge),
ArgumentMatchers.eq(onlineStatus));
// we are ONLINE!
// call it a few times - ensure at the end of the test that "ONLINE" status is raised only 2 times and not all
// the time
handler.handleDataResult(getDataResult());
handler.handleDataResult(getDataResult());
handler.handleDataResult(getDataResult());
// simulate one wrong data result
handler.handleDataFailure(getFailResult());
verify(callback).statusUpdated(ArgumentMatchers.eq((Thing) bridge), ArgumentMatchers.eq(offlineStatus));
// // go online again
handler.handleDataResult(getDataResult());
verify(callback, times(2)).statusUpdated(ArgumentMatchers.eq((Thing) bridge),
ArgumentMatchers.eq(onlineStatus));
}
private AsyncModbusReadResult getInfoResult() {
byte[] infoBlockBytes = new byte[] { -29, -36, 1, 2, 0, -120, 69, 51, 47, 68, 67, 32, 71, 109, 98, 72, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 49, 48, 32, 69, 32, 65, 73, 79, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 78, 73, 78, 73, 84, 73, 65, 76, 73, 90, 69,
68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 49, 48, 95, 50, 48, 50, 48, 95, 48, 52,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
ByteBuffer infoWrap = ByteBuffer.wrap(infoBlockBytes);
ModbusRegister[] infoBlock = new ModbusRegister[infoBlockBytes.length / 2];
for (int i = 0; i < infoBlock.length; i++) {
infoBlock[i] = new ModbusRegister(infoWrap.get(), infoWrap.get());
}
ModbusReadRequestBlueprint readRequest = mock(ModbusReadRequestBlueprint.class);
return new AsyncModbusReadResult(readRequest, new ModbusRegisterArray(infoBlock));
}
private AsyncModbusReadResult getDataResult() {
byte[] dataBlockBytes = new byte[] { 0, -14, 0, 0, -2, -47, -1, -1, 2, 47, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 99, 99, 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 125, 2, 21, 0, 0, 0, 27, 0, 26, 0, 0, 0, 103, 0, -117, 0, 0 };
ByteBuffer dataWrap = ByteBuffer.wrap(dataBlockBytes);
ModbusRegister[] dataBlock = new ModbusRegister[dataBlockBytes.length / 2];
for (int i = 0; i < dataBlock.length; i++) {
dataBlock[i] = new ModbusRegister(dataWrap.get(), dataWrap.get());
}
ModbusReadRequestBlueprint readRequest = mock(ModbusReadRequestBlueprint.class);
return new AsyncModbusReadResult(readRequest, new ModbusRegisterArray(dataBlock));
}
private AsyncModbusFailure<ModbusReadRequestBlueprint> getFailResult() {
ModbusReadRequestBlueprint readRequest = mock(ModbusReadRequestBlueprint.class);
return new AsyncModbusFailure<ModbusReadRequestBlueprint>(readRequest, new Exception("Something failed!"));
}
}

View File

@@ -0,0 +1,40 @@
/**
* Copyright (c) 2010-2020 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.e3dc.modbus;
import static org.junit.Assert.assertEquals;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Test;
import org.openhab.binding.modbus.e3dc.internal.modbus.Data.DataType;
import org.openhab.binding.modbus.e3dc.internal.modbus.Parser;
/**
* The {@link ParserNameTest} Tests for ModbusCallbacks
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class ParserNameTest {
@Test
public void testDebugNames() {
Parser mcInfo = new Parser(DataType.INFO);
assertEquals("Debug Name Info", "org.openhab.binding.modbus.e3dc.internal.modbus.Parser:INFO",
mcInfo.toString());
Parser mcPower = new Parser(DataType.DATA);
assertEquals("Debug Name Power", "org.openhab.binding.modbus.e3dc.internal.modbus.Parser:DATA",
mcPower.toString());
}
}

View File

@@ -0,0 +1,61 @@
/**
* Copyright (c) 2010-2020 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.e3dc.util;
import static org.junit.Assert.assertEquals;
import java.nio.ByteBuffer;
import java.util.BitSet;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Test;
import org.openhab.binding.modbus.e3dc.internal.dto.DataConverter;
/**
* The {@link DataConverterTest} Test data conversions
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class DataConverterTest {
@Test
public void testE3DCSwapValueNegative() {
// Reg 69 value 65098 bytes [-2, 74]
// Reg 70 value 65535 bytes [-1, -1]
byte[] b = new byte[] { -2, -74, -1, -1 };
assertEquals("Negative Value", -330, DataConverter.getInt32Swap(ByteBuffer.wrap(b)));
}
@Test
public void testBitset() {
byte[] b = new byte[] { 3, 16 };
BitSet s = BitSet.valueOf(b);
assertEquals("Bit0", true, s.get(0));
assertEquals("Bit1", true, s.get(1));
assertEquals("Bit2", false, s.get(2));
assertEquals("Bit3", false, s.get(3));
assertEquals("Bit4", false, s.get(4));
assertEquals("Bit5", false, s.get(5));
assertEquals("Bit6", false, s.get(6));
assertEquals("Bit7", false, s.get(7));
assertEquals("Bit8", false, s.get(8));
assertEquals("Bit9", false, s.get(9));
assertEquals("Bit10", false, s.get(10));
assertEquals("Bit11", false, s.get(11));
assertEquals("Bit12", true, s.get(12));
assertEquals("Bit13", false, s.get(13));
assertEquals("Bit14", false, s.get(14));
assertEquals("Bit15", false, s.get(15));
}
}