Skip to content

Commit

Permalink
[dsmr] Added support for Belgium smartmeter
Browse files Browse the repository at this point in the history
Signed-off-by: Hilbrand Bouwkamp <[email protected]>
  • Loading branch information
Hilbrand committed Dec 9, 2019
1 parent 12b2190 commit cb0939b
Show file tree
Hide file tree
Showing 16 changed files with 382 additions and 143 deletions.
279 changes: 146 additions & 133 deletions bundles/org.openhab.binding.dsmr/README.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class CosemHexString extends CosemValueDescriptor<StringType> {
*/
@Override
protected StringType getStateValue(String cosemValue) throws ParseException {
final String cosemHexValue = cosemValue.replaceAll("\\r\\n", "");
final String cosemHexValue = cosemValue.replaceAll("\\r\\n", "").trim();

if (cosemHexValue.length() % 2 != 0) {
throw new ParseException(cosemHexValue + " is not a valid hexadecimal string", 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ public class CosemObjectFactory {
*/
private final Map<OBISIdentifier, CosemObjectType> obisLookupTableFixed;

/**
* Lookup cache for fixed OBIS Identifiers that has the same id for different data types
*/
private final Map<OBISIdentifier, List<CosemObjectType>> obisLookupTableMultipleFixed;

/**
* Lookup cache for dynamic OBIS Identifiers
*/
Expand Down Expand Up @@ -67,12 +72,16 @@ public CosemObjectFactory() {
* correct OBISIdentifier is discovered for a certain OBISMsgType this is added to the obisLookupTableDynamic.
*/
obisLookupTableFixed = new HashMap<>();
obisLookupTableMultipleFixed = new HashMap<>();
obisLookupTableDynamic = new HashMap<>();
obisWildcardCosemTypeList = new ArrayList<>();

for (CosemObjectType msgType : CosemObjectType.values()) {
if (msgType.obisId.reducedOBISIdentifierIsWildCard()) {
obisWildcardCosemTypeList.add(msgType);
} else if (msgType.obisId.isConflict()) {
obisLookupTableMultipleFixed.computeIfAbsent(msgType.obisId, r -> new ArrayList<CosemObjectType>())
.add(msgType);
} else {
obisLookupTableFixed.put(msgType.obisId, msgType);
}
Expand Down Expand Up @@ -106,6 +115,14 @@ public CosemObjectFactory() {
if (obisLookupTableFixed.containsKey(reducedObisId)) {
cosemObject = getCosemObjectInternal(obisLookupTableFixed.get(reducedObisId), obisId, cosemStringValues);
logger.trace("Found obisId {} in the fixed lookup table", reducedObisId);
} else if (obisLookupTableMultipleFixed.containsKey(reducedObisId)) {
for (CosemObjectType cosemObjectType : obisLookupTableMultipleFixed.get(reducedObisId)) {
cosemObject = getCosemObjectInternal(cosemObjectType, obisId, cosemStringValues);
if (cosemObject != null) {
logger.trace("Found obisId {} in the fixed lookup table", reducedObisId);
break;
}
}
} else if (obisLookupTableDynamic.containsKey(reducedObisId)) {
logger.trace("Found obisId {} in the dynamic lookup table", reducedObisId);
cosemObject = getCosemObjectInternal(obisLookupTableDynamic.get(reducedObisId), obisId, cosemStringValues);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public enum CosemObjectType {

/* General messages */
P1_VERSION_OUTPUT(new OBISIdentifier(1, 3, 0, 2, 8, null), CosemString.INSTANCE),
P1_EMUCS_VERSION_OUTPUT(new OBISIdentifier(0, 0, 96, 1, 4, null), CosemString.INSTANCE),
P1_TIMESTAMP(new OBISIdentifier(0, 0, 1, 0, 0, null), new CosemDate("")),
P1_TEXT_CODE(new OBISIdentifier(0, 0, 96, 13, 1, null), CosemHexString.INSTANCE),
P1_TEXT_STRING(new OBISIdentifier(0, 0, 96, 13, 0, null), CosemHexString.INSTANCE),
Expand Down Expand Up @@ -70,8 +71,8 @@ public enum CosemObjectType {
EMETER_ACTUAL_DELIVERY(new OBISIdentifier(1, 0, 1, 7, 0, null), CosemQuantity.KILO_WATT),
EMETER_ACTUAL_PRODUCTION(new OBISIdentifier(1, 0, 2, 7, 0, null), CosemQuantity.KILO_WATT),
EMETER_TRESHOLD_A_V2_1(new OBISIdentifier(1, 0, 17, 0, 0, null), CosemQuantity.AMPERE),
EMETER_TRESHOLD_A(new OBISIdentifier(0, 0, 17, 0, 0, null), CosemQuantity.AMPERE),
EMETER_TRESHOLD_KWH(new OBISIdentifier(0, 0, 17, 0, 0, null), CosemQuantity.KILO_WATT),
EMETER_TRESHOLD_A(new OBISIdentifier(0, 0, 17, 0, 0, null, true), CosemQuantity.AMPERE),
EMETER_TRESHOLD_KWH(new OBISIdentifier(0, 0, 17, 0, 0, null, true), CosemQuantity.KILO_WATT),
EMETER_SWITCH_POSITION_V2_1(new OBISIdentifier(1, 0, 96, 3, 10, null), CosemDecimal.INSTANCE),
EMETER_SWITCH_POSITION(new OBISIdentifier(0, 0, 96, 3, 10, null), CosemDecimal.INSTANCE),
EMETER_POWER_FAILURES(new OBISIdentifier(0, 0, 96, 7, 21, null), CosemDecimal.INSTANCE),
Expand Down Expand Up @@ -104,6 +105,7 @@ public enum CosemObjectType {
GMETER_24H_DELIVERY_V2(new OBISIdentifier(7, 0, 23, 1, 0, null), CosemQuantity.CUBIC_METRE, CosemDate.INSTANCE),
GMETER_24H_DELIVERY_COMPENSATED_V2(new OBISIdentifier(7, 0, 23, 2, 0, null), CosemQuantity.CUBIC_METRE,
CosemDate.INSTANCE),
GMETER_LAST_VALUE(new OBISIdentifier(0, null, 24, 2, 3, null), CosemDate.INSTANCE, CosemQuantity.CUBIC_METRE),
GMETER_VALUE_V3(new OBISIdentifier(0, null, 24, 3, 0, null), CosemDate.INSTANCE, // Time stamp off the reading
new CosemString("val1"), // Specification is not clear what this value is
new CosemDecimal("val2"), // Specification is not clear what this value is
Expand Down Expand Up @@ -143,7 +145,7 @@ public enum CosemObjectType {
// The actual reactive's and threshold have no unit in the data and therefore are not quantity types.
EMETER_ACTUAL_REACTIVE_DELIVERY(new OBISIdentifier(1, 0, 3, 7, 0, null), CosemDecimal.INSTANCE),
EMETER_ACTUAL_REACTIVE_PRODUCTION(new OBISIdentifier(1, 0, 4, 7, 0, null), CosemDecimal.INSTANCE),
EMETER_ACTIVE_THRESHOLD_SMAX(new OBISIdentifier(0, 0, 17, 0, 0, null), CosemDecimal.INSTANCE),
EMETER_ACTIVE_THRESHOLD_SMAX(new OBISIdentifier(0, 0, 17, 0, 0, null, true), CosemDecimal.INSTANCE),
EMETER_INSTANT_REACTIVE_POWER_DELIVERY_L1(new OBISIdentifier(1, 0, 23, 7, 0, null), CosemQuantity.KILO_VAR),
EMETER_INSTANT_REACTIVE_POWER_DELIVERY_L2(new OBISIdentifier(1, 0, 43, 7, 0, null), CosemQuantity.KILO_VAR),
EMETER_INSTANT_REACTIVE_POWER_DELIVERY_L3(new OBISIdentifier(1, 0, 63, 7, 0, null), CosemQuantity.KILO_VAR),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ public class OBISIdentifier {
private @Nullable Integer groupE;
private @Nullable Integer groupF;

private boolean conflict;

/**
* Constructs a new OBIS Identifier (A-B:C.D.E.F)
*
Expand All @@ -58,12 +60,29 @@ public class OBISIdentifier {
*/
public OBISIdentifier(int groupA, @Nullable Integer groupB, int groupC, int groupD, @Nullable Integer groupE,
@Nullable Integer groupF) {
this(groupA, groupB, groupC, groupD, groupE, groupF, false);
}

/**
* Constructs a new OBIS Identifier (A-B:C.D.E.F)
*
* @param groupA A value
* @param groupB B value
* @param groupC C value
* @param groupD D value
* @param groupE E value
* @param groupF F value
* @param conflict if true indicates this OBIS Identifier is used for different types of data.
*/
public OBISIdentifier(int groupA, @Nullable Integer groupB, int groupC, int groupD, @Nullable Integer groupE,
@Nullable Integer groupF, boolean conflict) {
this.groupA = groupA;
this.groupB = groupB;
this.groupC = groupC;
this.groupD = groupD;
this.groupE = groupE;
this.groupF = groupF;
this.conflict = conflict;
}

/**
Expand Down Expand Up @@ -104,6 +123,10 @@ public OBISIdentifier(String obisIDString) throws ParseException {
}
}

public boolean isConflict() {
return conflict;
}

/**
* @return the groupA
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,31 @@ public enum DSMRMeterType {
CosemObjectType.EMETER_INSTANT_REACTIVE_POWER_DELIVERY_L1, CosemObjectType.EMETER_INSTANT_REACTIVE_POWER_DELIVERY_L2,
CosemObjectType.EMETER_INSTANT_REACTIVE_POWER_DELIVERY_L3, CosemObjectType.EMETER_INSTANT_REACTIVE_POWER_PRODUCTION_L1,
CosemObjectType.EMETER_INSTANT_REACTIVE_POWER_PRODUCTION_L2, CosemObjectType.EMETER_INSTANT_REACTIVE_POWER_PRODUCTION_L3,
});
}),
/** Belgium Smart Meter for the e-MUCS specification */
DEVICE_EMUCS_V1_0(DSMRMeterKind.DEVICE, CosemObjectType.UNKNOWN,
CosemObjectType.P1_TEXT_STRING, CosemObjectType.P1_TEXT_STRING, CosemObjectType.P1_EMUCS_VERSION_OUTPUT,
CosemObjectType.P1_TIMESTAMP),

/** Belgium Smart Electricity Meter for the e-MUCS specification */
ELECTRICITY_EMUCS_V1_0(DSMRMeterKind.MAIN_ELECTRICITY, CosemObjectType.EMETER_EQUIPMENT_IDENTIFIER,
new CosemObjectType[] {
CosemObjectType.EMETER_EQUIPMENT_IDENTIFIER, CosemObjectType.EMETER_DELIVERY_TARIFF1,
CosemObjectType.EMETER_DELIVERY_TARIFF2, CosemObjectType.EMETER_PRODUCTION_TARIFF1,
CosemObjectType.EMETER_PRODUCTION_TARIFF2, CosemObjectType.EMETER_TARIFF_INDICATOR,
CosemObjectType.EMETER_ACTUAL_DELIVERY, CosemObjectType.EMETER_ACTUAL_PRODUCTION,
CosemObjectType.EMETER_TRESHOLD_KWH, CosemObjectType.EMETER_SWITCH_POSITION},
new CosemObjectType[] {
CosemObjectType.EMETER_INSTANT_CURRENT_L1, CosemObjectType.EMETER_INSTANT_CURRENT_L2,
CosemObjectType.EMETER_INSTANT_CURRENT_L3, CosemObjectType.EMETER_INSTANT_VOLTAGE_L1,
CosemObjectType.EMETER_INSTANT_VOLTAGE_L2, CosemObjectType.EMETER_INSTANT_VOLTAGE_L3
}),

/** Belgium Smart Gas Meter for the e-MUCS specification */
GAS_EMUCS_V1_0(DSMRMeterKind.GAS, CosemObjectType.EMETER_EQUIPMENT_IDENTIFIER,
new CosemObjectType[] {
CosemObjectType.EMETER_EQUIPMENT_IDENTIFIER, CosemObjectType.METER_DEVICE_TYPE,
CosemObjectType.GMETER_LAST_VALUE, CosemObjectType.METER_VALVE_SWITCH_POSITION });
// @formatter:on

public static final Set<ThingTypeUID> METER_THING_TYPES = Arrays.asList(DSMRMeterType.values()).stream()
Expand Down Expand Up @@ -377,6 +401,8 @@ public DSMRMeterDescriptor isCompatible(Map<CosemObjectType, CosemObject> availa
if (!availableCosemObjects.containsKey(objectType)) {
logger.trace("Required objectType {} not found", objectType);
return null;
} else {
logger.trace("FOUND Required objectType {}", objectType);
}
CosemObject cosemObject = availableCosemObjects.get(objectType);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ error.connector.read_error = Leesfout.

# thing types
thing-type.dsmr.dsmrBridge.label = Slimme Meter
thing-type.dsmr.dsmrBridge.description = De Nederlandse Slimme Meter (DSMR)
thing-type.dsmr.dsmrBridge.description = De Slimme Meter (DSMR Nederland, e-MUCS Belgie)
thing-type.dsmr.smartyBridge.label = Smarty Meter
thing-type.dsmr.smartyBridge.description = De luxemburgse Slimme Meter 'Smarty'

Expand Down Expand Up @@ -131,8 +131,12 @@ channel-type.dsmr.deliveryTariff0Type.label = Elektriciteitsverbruik Tarief 0
channel-type.dsmr.deliveryTariff0Type.description = Het totale elektriciteitsverbruik voor tarief 0.
channel-type.dsmr.deliveryTariff1Type.label = Elektriciteitsverbruik Daltarief
channel-type.dsmr.deliveryTariff1Type.description = Het totale elektriciteitsverbruik voor dal/nacht tarief 1.
channel-type.dsmr.deliveryTariff1BelgiumType.label = Elektriciteitsverbruik Piektarief
channel-type.dsmr.deliveryTariff1BelgiumType.description = Het totale elektriciteitsverbruik voor piek/dag tarief 1.
channel-type.dsmr.deliveryTariff2Type.label = Elektriciteitsverbruik Piektarief
channel-type.dsmr.deliveryTariff2Type.description = Het totale elektriciteitsverbruik voor piek/dag tarief 2.
channel-type.dsmr.deliveryTariff2BelgiumType.label = Elektriciteitsverbruik Daltarief
channel-type.dsmr.deliveryTariff2BelgiumType.description = Het totale elektriciteitsverbruik voor dal/nacht tarief 2.
channel-type.dsmr.deliveryTariff0AntiFraudType.label = Elektriciteitsverbruik Tarief 0
channel-type.dsmr.deliveryTariff0AntiFraudType.description = Het totale elektriciteitsverbruik voor tarief 0 (anti-fraude).
channel-type.dsmr.deliveryTariff1AntiFraudType.label = Elektriciteitsverbruik Daltarief
Expand All @@ -143,8 +147,12 @@ channel-type.dsmr.productionTariff0Type.label = Teruggeleverde Elektriciteit Tar
channel-type.dsmr.productionTariff0Type.description = De totale teruggeleverde elektriciteit voor tarief 0.
channel-type.dsmr.productionTariff1Type.label = Teruggeleverde Elektriciteit Daltarief
channel-type.dsmr.productionTariff1Type.description = De totale teruggeleverde elektriciteit voor dal/nacht tarief (1).
channel-type.dsmr.productionTariff1BelgiumType.label = Injectie Piektarief
channel-type.dsmr.productionTariff1BelgiumType.description = De totale injectie elektriciteit voor piek/dag tarief (1).
channel-type.dsmr.productionTariff2Type.label = Teruggeleverde Elektriciteit Piektarief
channel-type.dsmr.productionTariff2Type.description = De totale teruggeleverde elektriciteit voor piek/dag tarief (2).
channel-type.dsmr.productionTariff2BelgiumType.label = Injectie Daltarief
channel-type.dsmr.productionTariff2BelgiumType.description = De totale injectie elektriciteit voor dal/nacht tarief (2).
channel-type.dsmr.tariffIndicatorType.label = Tarief Indicator
channel-type.dsmr.tariffIndicatorType.description = De tarief indicatie van het huidge tarief (1 = dal/nacht, 2 = piek/dag).
channel-type.dsmr.activeImportPowerType.label = Opgetelde Huidige Elektriciteit
Expand Down Expand Up @@ -212,6 +220,11 @@ channel-type.dsmr.gasCompensatedDelivery24HType.label = Gecompenseerd Gasverbrui
channel-type.dsmr.gasCompensatedDelivery24HType.description = Het totaal gecompenseerd gasverbruik in kubieke meter (m3) in de afgelopen 24 uur.
channel-type.dsmr.gasDeliveryType.label = Gasverbruik
channel-type.dsmr.gasDeliveryType.description = Het totaal aantal kubieke meter (m3) gasverbruik in de afgelopen periode.
channel-type.dsmr.gasLastDeliveryType.label = Gasverbruik
channel-type.dsmr.gasLastDeliveryType.description = Het totaal niet temperatuur gecorrigeerd aantal kubieke meter (m3) gasverbruik.
channel-type.dsmr.gasLastTimestampType.label = Tijd Gasmeting
channel-type.dsmr.gasLastTimestampType.description = Tijd van de laatste niet temperatuur gecorrigeerd gasverbruik registratie.

channel-type.dsmr.gasValvePositionType.label = Gasklepstand
channel-type.dsmr.gasValvePositionType.description = De stand van de gasklep.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@
<description>The total amount of electricity used for tariff 2.</description>
<state pattern="%.1f %unit%" readOnly="true" />
</channel-type>
<channel-type id="deliveryTariff1BelgiumType">
<item-type>Number:Energy</item-type>
<label>Delivery Tariff 1</label>
<description>The total amount of electricity used for tariff 1.</description>
<state pattern="%.1f %unit%" readOnly="true" />
</channel-type>
<channel-type id="deliveryTariff2BelgiumType">
<item-type>Number:Energy</item-type>
<label>Delivery Tariff 2</label>
<description>The total amount of electricity used for tariff 2.</description>
<state pattern="%.1f %unit%" readOnly="true" />
</channel-type>
<channel-type id="deliveryTariff0AntiFraudType" advanced="true">
<item-type>Number:Energy</item-type>
<label>Delivery Tariff 0</label>
Expand Down Expand Up @@ -64,6 +76,18 @@
<description>The total amount of electricity produced for tariff 2.</description>
<state pattern="%.1f %unit%" readOnly="true" />
</channel-type>
<channel-type id="productionTariff1BelgiumType" advanced="true">
<item-type>Number:Energy</item-type>
<label>Production Tariff 1</label>
<description>The total amount of electricity produced for tariff 1.</description>
<state pattern="%.1f %unit%" readOnly="true" />
</channel-type>
<channel-type id="productionTariff2BelgiumType" advanced="true">
<item-type>Number:Energy</item-type>
<label>Production Tariff 2</label>
<description>The total amount of electricity produced for tariff 2.</description>
<state pattern="%.1f %unit%" readOnly="true" />
</channel-type>
<channel-type id="totalImportedEnergyRegisterPType">
<item-type>Number:Energy</item-type>
<label>Total Imported Energy (P+)</label>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@
<description>The total amount used in the past period.</description>
<state pattern="%.3f %unit%" readOnly="true"></state>
</channel-type>
<channel-type id="gasLastDeliveryType">
<item-type>Number:Volume</item-type>
<label>Gas Delivery</label>
<description>Last value of not temperature corrected gas volume.</description>
<state pattern="%.3f %unit%" readOnly="true"></state>
</channel-type>
<channel-type id="gasLastTimestampType">
<item-type>DateTime</item-type>
<label>Timestamp</label>
<description>Timestamp of the last gas meter capture time.</description>
<state readOnly="true"></state>
</channel-type>
<channel-type id="gasValvePositionType">
<item-type>Number</item-type>
<label>Gas Valve Position</label>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<bridge-type id="dsmrBridge">
<label>Smart Meter</label>
<description>The Dutch Smart Meter (DSMR)</description>
<description>The Dutch/Belgium Smart Meter (DSMR/e-MUCS)</description>

<config-description-ref uri="thing-type:dsmr:bridgesettings" />
</bridge-type>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="dsmr"
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="device_v1_0" listed="false">
<supported-bridge-type-refs>
<bridge-type-ref id="dsmrBridge" />
</supported-bridge-type-refs>

<label>Device Meter (e-MUCS V1.0)</label>
<description>This is the device meter that complies to the e-MUCS 1.0 specification.</description>

<channels>
<channel id="p1_text_string" typeId="p1TextStringType" />
<channel id="p1_emucs_version_output" typeId="p1VersionType" />
<channel id="p1_timestamp" typeId="p1TimestampType" />
</channels>
<config-description-ref uri="thing-type:dsmr:meterdescriptor" />
</thing-type>
</thing:thing-descriptions>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="dsmr"
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="electricity_emucs_v1_0" listed="false">
<supported-bridge-type-refs>
<bridge-type-ref id="dsmrBridge" />
</supported-bridge-type-refs>

<label>Electricity Meter (e-MUCS V1.0)</label>
<description>This is an electricity meter that complies to the e-MUCS V1.0 specification.</description>

<channels>
<channel id="emeter_equipment_identifier" typeId="equipmentIdType" />
<channel id="emeter_delivery_tariff1" typeId="deliveryTariff1BelgiumType" />
<channel id="emeter_delivery_tariff2" typeId="deliveryTariff2BelgiumType" />
<channel id="emeter_production_tariff1" typeId="productionTariff1BelgiumType" />
<channel id="emeter_production_tariff2" typeId="productionTariff2BelgiumType" />
<channel id="emeter_tariff_indicator" typeId="tariffIndicatorType" />
<channel id="emeter_actual_delivery" typeId="actualDeliveryType" />
<channel id="emeter_actual_production" typeId="actualProductionType" />
<channel id="emeter_switch_position" typeId="switchPositionType" />
<channel id="emeter_treshold_kw" typeId="actualTresholdkWType" />
<channel id="emeter_instant_current_l1" typeId="instantCurrentL1Type" />
<channel id="emeter_instant_current_l2" typeId="instantCurrentL2Type" />
<channel id="emeter_instant_current_l3" typeId="instantCurrentL3Type" />
<channel id="emeter_instant_voltage_l1" typeId="instantVoltageL1Type" />
<channel id="emeter_instant_voltage_l2" typeId="instantVoltageL2Type" />
<channel id="emeter_instant_voltage_l3" typeId="instantVoltageL3Type" />
</channels>
<config-description-ref uri="thing-type:dsmr:meterdescriptor" />
</thing-type>
</thing:thing-descriptions>
Loading

0 comments on commit cb0939b

Please sign in to comment.