diff --git a/addons/binding/org.openhab.binding.dsmr.test/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.dsmr.test/META-INF/MANIFEST.MF index 30f6f17cf7443..115ccb6a500a3 100644 --- a/addons/binding/org.openhab.binding.dsmr.test/META-INF/MANIFEST.MF +++ b/addons/binding/org.openhab.binding.dsmr.test/META-INF/MANIFEST.MF @@ -11,28 +11,15 @@ Fragment-Host: org.openhab.binding.dsmr Import-Package: org.apache.commons.io, org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.core.common, - org.eclipse.smarthome.core.common.registry, - org.eclipse.smarthome.core.events, org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.util, org.eclipse.smarthome.io.transport.serial, - org.eclipse.smarthome.test, - org.eclipse.smarthome.test.java, - org.eclipse.smarthome.test.storage, org.hamcrest;core=split, org.hamcrest.core, org.junit, org.mockito, org.mockito.hamcrest, org.mockito.invocation, - org.mockito.stubbing, - org.osgi.framework, - org.osgi.service.component, - org.osgi.service.device, - org.slf4j + org.mockito.stubbing Require-Bundle: org.hamcrest, org.junit diff --git a/addons/binding/org.openhab.binding.dsmr.test/org.openhab.binding.dsmr.test.launch b/addons/binding/org.openhab.binding.dsmr.test/org.openhab.binding.dsmr.test.launch index 6bac0ee1f82ba..a0180cf9ce4da 100644 --- a/addons/binding/org.openhab.binding.dsmr.test/org.openhab.binding.dsmr.test.launch +++ b/addons/binding/org.openhab.binding.dsmr.test/org.openhab.binding.dsmr.test.launch @@ -34,8 +34,8 @@ - - + + diff --git a/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/TelegramReaderUtil.java b/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/TelegramReaderUtil.java index 5deb5232136c2..dea925a61c35f 100644 --- a/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/TelegramReaderUtil.java +++ b/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/TelegramReaderUtil.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.dsmr.internal; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import java.io.IOException; import java.io.InputStream; @@ -61,7 +61,9 @@ public static P1Telegram readTelegram(String telegramName, TelegramState expecte byte[] telegram = readRawTelegram(telegramName); P1TelegramParser parser = new P1TelegramParser(p1Telegram::set); - parser.parseData(telegram, 0, telegram.length); + parser.setLenientMode(true); + parser.parse(telegram, telegram.length); + assertNotNull("Telegram state should have been set. (Missing newline at end of message?)", p1Telegram.get()); assertEquals("Expected TelegramState should be as expected", expectedTelegramState, p1Telegram.get().getTelegramState()); return p1Telegram.get(); diff --git a/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDeviceTest.java b/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDeviceTest.java index ee4ff8cf0292b..ef2b4018f6647 100644 --- a/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDeviceTest.java +++ b/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDeviceTest.java @@ -99,7 +99,7 @@ public void handleErrorEvent(@NonNull DSMRConnectorErrorEvent connectorErrorEven try (InputStream inputStream = new ByteArrayInputStream(TelegramReaderUtil.readRawTelegram(TELEGRAM_NAME))) { when(mockSerialPort.getInputStream()).thenReturn(inputStream); DSMRSerialAutoDevice device = new DSMRSerialAutoDevice(serialPortManager, DUMMY_PORTNAME, listener, - scheduler, 1); + new DSMRTelegramListener(), scheduler, 1); device.start(); assertSame("Expect to be starting discovery state", DeviceState.DISCOVER_SETTINGS, device.getState()); serialPortEventListener @@ -136,7 +136,7 @@ public void handleErrorEvent(@NonNull DSMRConnectorErrorEvent connectorErrorEven throw new PortInUseException(); }).when(mockIdentifier).open(eq(DSMRBindingConstants.DSMR_PORT_NAME), anyInt()); DSMRSerialAutoDevice device = new DSMRSerialAutoDevice(serialPortManager, DUMMY_PORTNAME, listener, - scheduler, 1); + new DSMRTelegramListener(), scheduler, 1); device.start(); assertSame("Expected an error", DSMRConnectorErrorEvent.IN_USE, eventRef.get()); assertSame("Expect to be in error state", DeviceState.ERROR, device.getState()); @@ -146,7 +146,8 @@ public void handleErrorEvent(@NonNull DSMRConnectorErrorEvent connectorErrorEven assertSame("Expect to be starting discovery state", DeviceState.DISCOVER_SETTINGS, device.getState()); // Trigger device to go into error stage with port doesn't exist. mockIdentifier = null; - device = new DSMRSerialAutoDevice(serialPortManager, DUMMY_PORTNAME, listener, scheduler, 1); + device = new DSMRSerialAutoDevice(serialPortManager, DUMMY_PORTNAME, listener, new DSMRTelegramListener(), + scheduler, 1); device.start(); assertSame("Expected an error", DSMRConnectorErrorEvent.DONT_EXISTS, eventRef.get()); assertSame("Expect to be in error state", DeviceState.ERROR, device.getState()); diff --git a/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/device/SmartyDecrypterTest.java b/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/device/SmartyDecrypterTest.java new file mode 100644 index 0000000000000..ba71133639c76 --- /dev/null +++ b/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/device/SmartyDecrypterTest.java @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2010-2018 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.binding.dsmr.internal.device; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.assertThat; + +import java.nio.charset.StandardCharsets; +import java.util.concurrent.atomic.AtomicReference; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.Test; +import org.openhab.binding.dsmr.internal.TelegramReaderUtil; +import org.openhab.binding.dsmr.internal.device.p1telegram.P1TelegramListener; +import org.openhab.binding.dsmr.internal.device.p1telegram.P1TelegramParser; + +/** + * Test class for the {@link SmartyDecrypter}. + * + * @author Hilbrand Bouwkamp - Initial contribution + */ +@NonNullByDefault +public class SmartyDecrypterTest { + + private static final String KEY = "D491470F47126332B07D1923B3504188"; + private static final int[] TELEGRAM = new int[] { 0xDB, 0x08, 0x53, 0x41, 0x47, 0x67, 0x70, 0x01, 0xBD, 0x54, 0x82, + 0x02, 0x7A, 0x30, 0x00, 0x05, 0xA8, 0xE3, 0x80, 0x6E, 0xE6, 0xE6, 0x39, 0x27, 0x4C, 0x7B, 0xC5, 0x70, 0x95, + 0xF8, 0x72, 0xB0, 0x8D, 0xDE, 0x62, 0x1F, 0xB7, 0x4E, 0xE8, 0x1E, 0x5E, 0xBE, 0x34, 0x2C, 0x93, 0xD8, 0xE7, + 0x37, 0x81, 0xFB, 0x2A, 0x1E, 0xB8, 0x71, 0x00, 0x74, 0xA5, 0x4F, 0xC5, 0x7A, 0xA7, 0xD1, 0xD9, 0x92, 0x36, + 0xC4, 0x2E, 0x2E, 0xC0, 0x1A, 0x03, 0x24, 0xEF, 0xC7, 0xF0, 0x2E, 0x3B, 0xA2, 0xFA, 0x43, 0x19, 0x6C, 0xA6, + 0x03, 0x83, 0x8A, 0xB8, 0x32, 0x2D, 0xFF, 0xA8, 0x3F, 0x7E, 0x83, 0x93, 0x2E, 0x60, 0x07, 0x40, 0x6B, 0x11, + 0x2B, 0x41, 0x74, 0x5F, 0x38, 0x80, 0x56, 0x46, 0xD5, 0x3F, 0xEA, 0x7A, 0x02, 0x9F, 0xA7, 0x9C, 0x36, 0xD9, + 0xD7, 0x77, 0xDA, 0x8B, 0x5A, 0x03, 0xED, 0x3E, 0xC6, 0xE4, 0x85, 0xDE, 0xC0, 0xC9, 0x8D, 0x7D, 0x53, 0xC0, + 0xBD, 0x17, 0x5B, 0x22, 0x8E, 0x01, 0xF4, 0x66, 0xD9, 0x84, 0x37, 0x77, 0x80, 0x85, 0x03, 0xD6, 0x30, 0x15, + 0x8C, 0x82, 0x36, 0xD8, 0x68, 0x08, 0x8E, 0xB1, 0x39, 0x77, 0xBD, 0xFC, 0x47, 0x43, 0x67, 0xE5, 0x57, 0x13, + 0x6F, 0xEB, 0x6D, 0x53, 0x7F, 0x18, 0x30, 0x44, 0x84, 0xA0, 0x14, 0xB4, 0x90, 0x99, 0xA6, 0x3C, 0x18, 0xD4, + 0x30, 0x3A, 0x4C, 0x53, 0x85, 0x3A, 0x0C, 0xA8, 0xC5, 0xB0, 0xE3, 0xE8, 0x05, 0xBF, 0xD8, 0x42, 0x54, 0x10, + 0xFA, 0xB5, 0x22, 0xB3, 0x5A, 0x8F, 0x25, 0x8B, 0x57, 0xBB, 0x1E, 0x97, 0xE2, 0x48, 0x9B, 0x6F, 0x37, 0x07, + 0x17, 0x53, 0xAF, 0xFB, 0xB4, 0xEE, 0xBC, 0xA5, 0x02, 0xEE, 0xA5, 0x92, 0xB8, 0x80, 0x85, 0xAE, 0x15, 0x5B, + 0xCE, 0xA8, 0xF7, 0x32, 0xD6, 0xB0, 0x11, 0x5B, 0xB9, 0xCE, 0xD7, 0x6B, 0xE5, 0xB8, 0x93, 0xB2, 0xA7, 0xEF, + 0xC8, 0x16, 0x3C, 0x17, 0x1E, 0x77, 0xC3, 0xD2, 0x9A, 0x72, 0xB7, 0x47, 0x8E, 0xD6, 0xDF, 0xE2, 0xC4, 0x8B, + 0xF3, 0xF9, 0xD8, 0xF8, 0x95, 0xCA, 0x1C, 0xB4, 0x2E, 0xAA, 0xC4, 0xCB, 0x21, 0xD6, 0xEA, 0xB5, 0x1E, 0x77, + 0xE4, 0xD6, 0x02, 0x9D, 0x78, 0x84, 0x27, 0xDD, 0x5B, 0xFC, 0x46, 0xBD, 0xD3, 0xE8, 0xA3, 0x2D, 0xBB, 0x6F, + 0x93, 0xB4, 0x84, 0x2B, 0x07, 0x3E, 0x9B, 0x6F, 0xE6, 0xE5, 0xDF, 0xC0, 0x58, 0xB5, 0xF4, 0x54, 0xAA, 0x3E, + 0xF1, 0x62, 0xBD, 0xF9, 0x3C, 0xB1, 0xE0, 0xC4, 0x52, 0xDB, 0xB2, 0xBE, 0x4C, 0xB4, 0xC6, 0x7D, 0x16, 0x9E, + 0x2A, 0x30, 0x61, 0x8F, 0xA2, 0x16, 0x54, 0x41, 0xB6, 0xD3, 0xC2, 0x2F, 0x1C, 0x36, 0x27, 0xE3, 0x5F, 0xFF, + 0xCF, 0x9F, 0x19, 0x47, 0xD7, 0xA6, 0xAE, 0x94, 0x2F, 0xC2, 0x1E, 0x24, 0x6F, 0x0E, 0xFC, 0x45, 0x6A, 0x78, + 0x89, 0xC9, 0x61, 0xC9, 0x3E, 0xA0, 0x89, 0xEE, 0xF6, 0xD1, 0xA4, 0x40, 0x56, 0xBA, 0xAA, 0xB3, 0x52, 0xCB, + 0xEA, 0x7D, 0x7E, 0x20, 0x67, 0x57, 0xAF, 0x0D, 0x42, 0x70, 0x64, 0xB0, 0x58, 0xD5, 0x72, 0x35, 0xA7, 0x8F, + 0x8D, 0xB9, 0x1C, 0xE4, 0xD6, 0x0A, 0x3C, 0x6B, 0xAB, 0xD9, 0x9A, 0x61, 0x16, 0x9B, 0x17, 0x2F, 0x24, 0x14, + 0xF5, 0x65, 0xBD, 0xE0, 0x23, 0x96, 0x54, 0xEA, 0xC7, 0x75, 0x52, 0xFB, 0xCC, 0x2A, 0x2F, 0xA1, 0x02, 0xD7, + 0xCD, 0x00, 0x3D, 0xDB, 0xEB, 0x9C, 0x1F, 0x81, 0x1D, 0xBC, 0x69, 0x53, 0x76, 0x33, 0x6E, 0x2E, 0x9D, 0x6C, + 0xFC, 0x73, 0xFC, 0xB4, 0xA2, 0x94, 0x46, 0xBD, 0x7C, 0xE3, 0x17, 0x0E, 0x58, 0x56, 0x76, 0x6B, 0x25, 0xE8, + 0x20, 0x49, 0xEE, 0x55, 0x08, 0xEC, 0x18, 0x16, 0x68, 0x5F, 0x1D, 0xCE, 0xC2, 0x38, 0x19, 0x12, 0x16, 0xDF, + 0xFB, 0xA0, 0x64, 0x7D, 0x32, 0xA1, 0x55, 0x9D, 0x99, 0x64, 0x0A, 0x15, 0xF2, 0x4A, 0xB0, 0x21, 0x72, 0xDB, + 0x3D, 0x56, 0x44, 0x54, 0xE7, 0xA0, 0x34, 0x6C, 0x09, 0x93, 0x99, 0xB8, 0x77, 0x0A, 0x9B, 0xDA, 0xA6, 0x19, + 0xC7, 0xC5, 0x23, 0x05, 0x26, 0xD6, 0xB8, 0x04, 0x99, 0xA0, 0xEE, 0x0C, 0xD4, 0xFC, 0x46, 0xA6, 0x77, 0xAF, + 0x68, 0xEF, 0x4D, 0xEE, 0x9E, 0x76, 0xB7, 0x8A, 0xC8, 0xA0, 0x4F, 0x43, 0x1D, 0xB3, 0x98, 0x6F, 0xCF, 0xD4, + 0x5E, 0xE1, 0xF1, 0x07, 0x99, 0xAF, 0x0B, 0x79, 0x4A, 0x41, 0x03, 0x73, 0x5D, 0x6D, 0xDA, 0x38, 0x29, 0x17, + 0x3F, 0x80, 0xF1, 0x38, 0xA5, 0x30, 0x1A, 0xFD, 0xF1, 0xDB, 0x87, 0x74, 0xF0, 0xC4, 0x0E, 0x6E, 0x71, 0x26, + 0x4B, 0x33, 0x4A, 0x94, 0x3F, 0xC1, 0x05, 0x52, 0xB0, 0x75, 0x2F, 0x3A, 0x0D, 0x7F, 0xD8, 0x04, 0x58, 0x59, + 0x54, 0x08, 0xF2, 0x85, 0x0F, 0x17, }; + + /** + * Tests decrypting of a single complete Smarty telegram. + */ + @Test + public void testSmartyDecrypter() { + AtomicReference telegramResult = new AtomicReference(""); + P1TelegramListener telegramListener = telegram -> telegramResult.set(telegram.getRawTelegram()); + SmartyDecrypter decoder = new SmartyDecrypter(new P1TelegramParser(telegramListener), + new DSMRTelegramListener(KEY), KEY); + decoder.setLenientMode(true); + byte[] data = new byte[TELEGRAM.length]; + + for (int i = 0; i < TELEGRAM.length; i++) { + data[i] = (byte) TELEGRAM[i]; + } + + decoder.parse(data, data.length); + String expected = new String(TelegramReaderUtil.readRawTelegram("smarty"), StandardCharsets.UTF_8); + + assertThat("Should have correctly decrypted the telegram", telegramResult.get(), is(equalTo(expected))); + } +} diff --git a/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParserTest.java b/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParserTest.java index 5f44b0f49a2ea..1b099f3569db1 100644 --- a/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParserTest.java +++ b/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParserTest.java @@ -45,7 +45,7 @@ public static final List data() { { "Landis_Gyr_E350", 10, }, { "Landis_Gyr_ZCF110", 25, }, { "Sagemcom_XS210", 41, }, - + { "smarty", 24, }, }); } // @formatter:on @@ -59,7 +59,8 @@ public static final List data() { @Test public void testParsing() { P1Telegram telegram = TelegramReaderUtil.readTelegram(telegramName, TelegramState.OK); + assertEquals("Should not have any unknown cosem objects", 0, telegram.getUnknownCosemObjects().size()); assertEquals("Expected number of objects", numberOfCosemObjects, - telegram.getCosemObjects().stream().mapToInt(o -> o.getCosemValues().size()).sum()); + telegram.getCosemObjects().stream().mapToInt(co -> co.getCosemValues().size()).sum()); } } diff --git a/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDetectorTest.java b/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDetectorTest.java index d5deff9205769..906f8d98af10c 100644 --- a/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDetectorTest.java +++ b/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDetectorTest.java @@ -56,6 +56,7 @@ public static final List data() { { "Landis_Gyr_E350", EnumSet.of( DEVICE_V2_V3, ELECTRICITY_V3_0)}, { "Landis_Gyr_ZCF110", EnumSet.of( DEVICE_V4, ELECTRICITY_V4_2, M3_V5_0)}, { "Sagemcom_XS210", EnumSet.of( DEVICE_V4, ELECTRICITY_V4_2)}, + { "smarty", EnumSet.of( DEVICE_V5, ELECTRICITY_SMARTY_V1_0)}, }); } // @formatter:on @@ -71,15 +72,17 @@ public void testDetectMeters() { P1Telegram telegram = TelegramReaderUtil.readTelegram(telegramName, TelegramState.OK); DSMRMeterDetector detector = new DSMRMeterDetector(); Entry, Map> entry = detector - .detectMeters(telegram); + .detectMeters(telegram); Collection detectMeters = entry.getKey(); - assertEquals("Should detect correct number of meters", expectedMeters.size(), detectMeters.size()); + assertEquals("Should detect correct number of meters: " + Arrays.toString(detectMeters.toArray()), + expectedMeters.size(), detectMeters.size()); assertEquals("Should not have any undetected cosem objects", 0, entry.getValue().size()); + assertEquals("Should not have any unknown cosem objects", 0, telegram.getUnknownCosemObjects().size()); for (DSMRMeterType meter : expectedMeters) { assertEquals( - String.format("Meter '%s' not found: %s", meter, - Arrays.toString(detectMeters.toArray(new DSMRMeterDescriptor[0]))), - 1, detectMeters.stream().filter(e -> e.getMeterType() == meter).count()); + String.format("Meter '%s' not found: %s", meter, + Arrays.toString(detectMeters.toArray(new DSMRMeterDescriptor[0]))), + 1, detectMeters.stream().filter(e -> e.getMeterType() == meter).count()); } } diff --git a/addons/binding/org.openhab.binding.dsmr.test/src/test/resources/org/openhab/binding/dsmr/internal/smarty.telegram b/addons/binding/org.openhab.binding.dsmr.test/src/test/resources/org/openhab/binding/dsmr/internal/smarty.telegram new file mode 100644 index 0000000000000..cec79406ede19 --- /dev/null +++ b/addons/binding/org.openhab.binding.dsmr.test/src/test/resources/org/openhab/binding/dsmr/internal/smarty.telegram @@ -0,0 +1,31 @@ +/Lux5\253663629_D + +1-3:0.2.8(42) +0-0:1.0.0(180130102122W) +0-0:42.0.0(53414731303330373030313134303034) +1-0:1.8.0(000006.695*kWh) +1-0:2.8.0(000000.025*kWh) +1-0:3.8.0(000000.818*kvarh) +1-0:4.8.0(000002.745*kvarh) +1-0:1.7.0(00.000*kW) +1-0:2.7.0(00.000*kW) +1-0:3.7.0(00.000) +1-0:4.7.0(00.007) +0-0:17.0.0(77.376) +0-0:96.3.10(1) +0-0:96.7.21(00069) +1-0:32.32.0(00044) +1-0:52.32.0(00003) +1-0:72.32.0(00002) +1-0:32.36.0(00000) +1-0:52.36.0(00000) +1-0:72.36.0(00000) +0-0:96.13.0() +0-0:96.13.2() +0-0:96.13.3() +0-0:96.13.4() +0-0:96.13.5() +1-0:31.7.0(000*A) +1-0:51.7.0(000*A) +1-0:71.7.0(000*A) +!CFDE diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/binding/binding.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/binding/binding.xml index c3c815539778c..cb8a3d6caadd0 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/binding/binding.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/binding/binding.xml @@ -3,6 +3,6 @@ xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd"> DSMR Binding - This binding integrates Dutch Smart Meters + This binding integrates Dutch and Luxembourg Smart Meters Marcel Volaart, Hilbrand Bouwkamp diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/config/configuration_parameters.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/config/configuration_parameters.xml index 93368d48fa40f..87a6b1cc9aee4 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/config/configuration_parameters.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/config/configuration_parameters.xml @@ -5,12 +5,12 @@ xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd"> - + serial-port The serial port where the P1 port of the Smart Meter is connected (e.g. Linux: /dev/ttyUSB0, Windows: COM1) - + 30 The time period within results are expected in seconds @@ -61,6 +61,23 @@ + + + serial-port + + The serial port where the P1 port of the Smart Meter is connected (e.g. Linux: /dev/ttyUSB0, Windows: COM1) + + + + The Luxembourgian Smart meter decryption key. Ask for your energy grid operator for your Smart meter P1 key. + + + 30 + + The time period within results are expected in seconds + + + 60 diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/i18n/dsmr_en.properties b/addons/binding/org.openhab.binding.dsmr/ESH-INF/i18n/dsmr_en.properties index 2243333db9bb3..16879e0480c60 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/i18n/dsmr_en.properties +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/i18n/dsmr_en.properties @@ -17,10 +17,16 @@ meterKind.slave_electricity2.label = Slave Electricity Meter 2 # Connector error messages error.bridge.nodata = Not receiving data from meter. error.configuration.invalidmetertype = The thing could not be initialized. Delete and re-add thing if the problem persists. -error.configuration.incomplete = Incomplete configuration. Not all required configuration settings are set. +error.configuration.invalidsmartykey = The given Smarty decyption key is to short. The decyption key must be 32 characters long. error.thing.nodata = Not receiving data from meter. error.connector.dont_exists = Serial port does not exist. error.connector.in_use = Serial port is already in use. error.connector.internal_error = Unexpected error, possible bug. Please report. error.connector.not_compatible = Serial port is not compatible. error.connector.read_error = Read error. + +# thing types +thing-type.dsmr.dsmrBridge.label = Smart Meter +thing-type.dsmr.dsmrBridge.description = The Dutch Smart Meter (DSMR) +thing-type.dsmr.smartyBridge.label = Smarty Meter +thing-type.dsmr.smartyBridge.description = The Luxembourgian Smart Meter 'Smarty' diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/i18n/dsmr_nl.properties b/addons/binding/org.openhab.binding.dsmr/ESH-INF/i18n/dsmr_nl.properties index 1ccb8121380e4..bc735ecec8970 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/i18n/dsmr_nl.properties +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/i18n/dsmr_nl.properties @@ -19,7 +19,7 @@ meterKind.slave_electricity2.label = Extra Elektriciteitsmeter 2 # Connector error messages error.bridge.nodata = Geen gegevens ontvangen van de meter. error.configuration.invalidmetertype = Het ding kon niet worden ge�nitialiseerd. Verwijder en voeg het ding opnieuw toe als het probleem zich blijft voordoen. -error.configuration.incomplete = Onvolledige configuratie. Niet alle benodigde velden zijn ingevuld. +error.configuration.invalidsmartykey = De opgegeven Smarty decyptie key is niet correct. De decyptie key moet 32 karakters lang zijn. error.thing.nodata = Geen gegevens ontvangen van de meter. error.connector.dont_exists = Seri�le poort bestaat niet. error.connector.in_use = Seri�le poort is al in gebruik. @@ -29,7 +29,9 @@ error.connector.read_error = Leesfout. # thing types thing-type.dsmr.dsmrBridge.label = Slimme Meter -thing-type.dsmr.dsmrBridge.description = Nederlandse Slimme Meter (DSMR). +thing-type.dsmr.dsmrBridge.description = De Nederlandse Slimme Meter (DSMR) +thing-type.dsmr.smartyBridge.label = Smarty Meter +thing-type.dsmr.smartyBridge.description = De luxemburgse Slimme Meter 'Smarty' thing-type.dsmr.cooling_ace4000.label = Koudemeter (ACE4000) thing-type.dsmr.cooling_ace4000.description = Dit is een koudemeter die voldoet aan de ACE4000 GTMM Mk3 standaard. @@ -238,3 +240,77 @@ channel-type.waterValueType.label = Waterverbruik channel-type.waterValueType.description = Het totale waterverbruik in de afgelopen periode. channel-type.waterValvePositionType.label = Waterklepstand channel-type.waterValvePositionType.description = De stand van de waterklepafsluiter. + + + Total imported energy register (P+) 1, 0:1.8.0.255 2 3 F9(3,3) kWh x + Total exported energy register (P-) 1, 0:2.8.0.255 2 3 F9(3,3) kWh x + Total imported energy register (Q+) 1, 0:3.8.0.255 2 3 F9(3,3) kvarh x + Total exported energy register (Q-) 1, 0:4.8.0.255 2 3 F9(3,3) kvarh x + Instantaneous imported active power (P+) 1, 0:1.7.0.255 2 3 F5(3,3) kW x + Instantaneous exported active power (P-) 1, 0:2.7.0.255 2 3 F5(3,3) kW x + Instantaneous imported reactive power (Q+) 1, 0:3.7.0.255 2 3 F5(3,3) kvar x + Instantaneous exported reactive power (Q-) 1, 0:4.7.0.255 2 3 F5(3,3) kvar x + Active threshold (SMAX) 0, 0:17.0.0.255 3 71 F4(1,1) kVA x + Breaker control state 0, 0:96.3.10.255 3 70 I1 - x + Number of power failures 0, 0:96.7.21.255 2 1 F5(0,0) - + Number of voltage sags L1 0, 0:32.32.0.255 2 1 F5(0,0) - + Number of voltage sags L2 0, 0:52.32.0.255 2 1 F5(0,0) + + Number of voltage sags L3 + 0, 0:72.32.0.255 + Number of voltage swells L1 + 0, 0:32.36.0.255 + Number of voltage swells L2 + 0, 0:52.36.0.255 + Number of voltage swells L3 + 0, 0:72.36.0.255 + Long message E + -meter + 0, 0:96.13.0.255 + Long message channel x + 0, 0:96.13.x.255 + Instantaneous current L1 + 1, 0:31.7.0.255 + Instantaneous current L2 + 1, 0:51.7.0.255 + Instantaneous current L3 + 1, 0:71.7.0.255 + Instantaneous active power (P+) L1 + 1, 0:21.7.0.255 + Instantaneous active power (P+) L2 + 1, 0:41.7.0.255 + Instantaneous active power (P+) L3 + 1, 0:61.7.0.255 + Instantaneous active power (P-) L1 + 1, 0:22.7.0.255 + Instantaneous active power (P-) L2 + 1, 0:42.7.0.255 + Instantaneous active power (P-) L3 + 1, 0:62.7.0.255 + Instantaneous reactive power (Q+) L1 + 1, 0:23.7.0.255 + Instantaneous reactive power (Q+) L2 + 1, 0:43.7.0.255 + Instantaneous reactive power (Q+) L3 + 1, 0:63.7.0.255 + Instantaneous reactive power (Q-) L1 + 1, 0:24.7.0.255 + Instantaneous reactive power (Q-) L2 + 1, 0:44.7.0.255 + Instantaneous reactive power (Q-) L3 + 1, 0:64.7.0.255 + Device type channel x + 0, x:24.1.0.255 + Equipment Identifier channel x + 0, x:96.1.0.255 + Last Index capture + -time channel x + 0, x:24.2.1.255 + Last Index gas channel x + 0, x:24.2.1.255 + valve position gas channel x + 0, x:24.4.0.255 + Last Index water channel x + 0, x:24.2.1.255 + Last Index heat channel x + 0, x:24.2.1.255 diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/channeltypes_electricity.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/channeltypes_electricity.xml index 271930c0d85ac..684b446a364b2 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/channeltypes_electricity.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/channeltypes_electricity.xml @@ -64,6 +64,30 @@ The total amount of electricity produced for tariff 2. + + Number:Energy + + The Total imported energy register (P+). + + + + Number:Energy + + The Total exported energy register (P-). + + + + Number:Energy + + The Total imported energy register (Q+). + + + + Number:Energy + + The Total exported energy register (Q-). + + String @@ -88,6 +112,18 @@ The current power production. + + Number + + The current reactive power delivery. + + + + Number + + The current reactive power production. + + Number:ElectricCurrent @@ -100,6 +136,12 @@ The actual threshold. + + Number + + Active threshold (SMAX). + + Number @@ -228,6 +270,42 @@ Each entry has its own channel (emeter_power_failure_log_duration*x*, *x* = 0 - The instant power production L3. + + Number:Power + + The instant reactive power delivery L1. + + + + Number:Power + + The instant reactive power delivery L2. + + + + Number:Power + + The instant reactive power delivery L3. + + + + Number:Power + + The instant reactive power production L1. + + + + Number:Power + + The instant reactive power production L2. + + + + Number:Power + + The instant reactive power production L3. + + Number:ElectricPotential diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/dsmr_device.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/dsmr_device.xml index 9856a572b5866..fa7f3019e6594 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/dsmr_device.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/dsmr_device.xml @@ -4,8 +4,8 @@ xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd"> - - The Dutch Smart Meter device + + The Dutch Smart Meter (DSMR) diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_cooling_ace4000.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_cooling_ace4000.xml index 1db72c0e69ba0..bb765a748a847 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_cooling_ace4000.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_cooling_ace4000.xml @@ -1,9 +1,14 @@ - + + + + This is a cooling meter that complies to the ACE4000 GTMM Mk3 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_cooling_v2_2.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_cooling_v2_2.xml index e45227bb97a65..194cff6e9b940 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_cooling_v2_2.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_cooling_v2_2.xml @@ -1,9 +1,14 @@ - + + + + This is a cooling meter that complies to the DSMR V2.2 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_device_dsmr_v2_v3.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_device_dsmr_v2_v3.xml index 643959bb97499..d3b3059545700 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_device_dsmr_v2_v3.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_device_dsmr_v2_v3.xml @@ -1,5 +1,6 @@ - diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_device_dsmr_v4.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_device_dsmr_v4.xml index 998c6aec1892c..25adb1adc8d2a 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_device_dsmr_v4.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_device_dsmr_v4.xml @@ -7,6 +7,7 @@ + diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_device_dsmr_v5.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_device_dsmr_v5.xml index 76b891d0de763..e519d51c8fde9 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_device_dsmr_v5.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_device_dsmr_v5.xml @@ -7,6 +7,7 @@ + diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v4.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v4.xml index 8919007a113a8..e4b8c6bf46e8d 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v4.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v4.xml @@ -1,5 +1,6 @@ - diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v4_0_4.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v4_0_4.xml index bc25f2d3bae54..33c59678f9dd5 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v4_0_4.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v4_0_4.xml @@ -1,5 +1,6 @@ - diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v4_2.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v4_2.xml index da895c6c916a6..0867538debf40 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v4_2.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v4_2.xml @@ -1,5 +1,6 @@ - diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v5.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v5.xml index ced74b5456622..4591d58d25a6f 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v5.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v5.xml @@ -1,5 +1,6 @@ - diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave1_ace4000.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave1_ace4000.xml index 1d4794c6e7c91..0ecdbf5a72a7a 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave1_ace4000.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave1_ace4000.xml @@ -1,9 +1,14 @@ - + + + + This is the first slave electricity meter that complies to the ACE4000 GTMM Mk3 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave2_ace4000.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave2_ace4000.xml index 5566ed5e1c64d..1770bd83d098b 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave2_ace4000.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave2_ace4000.xml @@ -1,9 +1,14 @@ - + + + + This is the second slave electricity meter that complies to the ACE4000 GTMM Mk3 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave_v4.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave_v4.xml index d1f59d15b12d8..5ab826fa635e2 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave_v4.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave_v4.xml @@ -1,5 +1,6 @@ - diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave_v5.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave_v5.xml index 6933201a1d4c7..6b7b05a0113ed 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave_v5.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave_v5.xml @@ -1,9 +1,14 @@ - + + + + This is the slave electricity meter that complies to the DSMR 5.x specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_smarty_v1.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_smarty_v1.xml new file mode 100644 index 0000000000000..cfa7e5c09c221 --- /dev/null +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_smarty_v1.xml @@ -0,0 +1,72 @@ + + + + + + + + + + This is an electricity meter that complies to the Luxembourg's Smarty V1.0 specification. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_ace4000.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_ace4000.xml index 12aee8dfc3d1f..193daf35cbcdc 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_ace4000.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_ace4000.xml @@ -1,9 +1,14 @@ - + + + + This is a gas meter that complies to the ACE4000 GTMM Mk3 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_v2_1.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_v2_1.xml index 68b6c26618413..199d397e1d367 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_v2_1.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_v2_1.xml @@ -1,9 +1,14 @@ - + + + + This is a gas meter that complies to the DSMR V2.1 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_v2_2.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_v2_2.xml index f27299f6b6126..44975edfafeda 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_v2_2.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_v2_2.xml @@ -1,9 +1,14 @@ - + + + + This is a gas meter that complies to the DSMR V2.2 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_v3_0.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_v3_0.xml index 6544878666a74..573ca1e4615be 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_v3_0.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_v3_0.xml @@ -1,9 +1,14 @@ - + + + + This is a gas meter that complies to the DSMR V3.0 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_generic_v3_0.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_generic_v3_0.xml index 918b439a465b9..2d6b2e277c30d 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_generic_v3_0.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_generic_v3_0.xml @@ -1,9 +1,14 @@ - + + + + This is a generic meter that complies to the DSMR V3.0 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gj_v3_0.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gj_v3_0.xml index c73e8b47c978c..b04f8ad7969de 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gj_v3_0.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gj_v3_0.xml @@ -1,9 +1,14 @@ - + + + + This is a Giga Joule meter that complies to the DSMR V3.0 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gj_v4.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gj_v4.xml index c3285243da09c..8e9ede8ab28d6 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gj_v4.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gj_v4.xml @@ -1,9 +1,14 @@ - + + + + This is a Giga Joule meter that complies to the DSMR V4.x specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gj_v5_0.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gj_v5_0.xml index dfbda35e31e80..bc3f3b753cabc 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gj_v5_0.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gj_v5_0.xml @@ -1,9 +1,14 @@ - + + + + This is a Giga Joule meter that complies to the DSMR V5.0 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_heating_ace4000.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_heating_ace4000.xml index 72e81335f14b8..581f9b43ae697 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_heating_ace4000.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_heating_ace4000.xml @@ -1,9 +1,14 @@ - + + + + This is a heating meter that complies to the ACE4000 GTMM Mk3 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_heating_v2_2.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_heating_v2_2.xml index 229588ca719cb..e4e7cddd67215 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_heating_v2_2.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_heating_v2_2.xml @@ -1,9 +1,14 @@ - + + + + This is a heating meter that complies to the DSMR V2.2 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_m3_v4.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_m3_v4.xml index f192e2ea24e53..fac084be87501 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_m3_v4.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_m3_v4.xml @@ -1,9 +1,14 @@ - + + + + This is a m3 meter that complies to the DSMR V4.x specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_m3_v5_0.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_m3_v5_0.xml index 84cf160f12bb9..1cb83bf791b7e 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_m3_v5_0.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_m3_v5_0.xml @@ -1,9 +1,14 @@ - + + + + This is a m3 meter that complies to the DSMR V5.0 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_water_ace4000.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_water_ace4000.xml index 181cbb9b2e0a2..7c6dd14f7987c 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_water_ace4000.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_water_ace4000.xml @@ -1,9 +1,14 @@ - + + + + This is a water meter that complies to the ACE4000 GTMM Mk3 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_water_v2_2.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_water_v2_2.xml index 055aef5e2d348..9afc6e199ec08 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_water_v2_2.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_water_v2_2.xml @@ -1,9 +1,14 @@ - + + + + This is a water meter that complies to the DSMR V2.2 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_water_v3_0.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_water_v3_0.xml index 230c0a07cce36..b943afbf4f5ac 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_water_v3_0.xml +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_water_v3_0.xml @@ -1,9 +1,14 @@ - + + + + This is a water meter that complies to the DSMR V3.0 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/smarty_device.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/smarty_device.xml new file mode 100644 index 0000000000000..7a9da2e565b40 --- /dev/null +++ b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/smarty_device.xml @@ -0,0 +1,13 @@ + + + + + + The Luxembourgian Smart Meter 'Smarty' + + + + + diff --git a/addons/binding/org.openhab.binding.dsmr/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.dsmr/META-INF/MANIFEST.MF index 3b4e94a3ca023..e74dfdf525c1c 100644 --- a/addons/binding/org.openhab.binding.dsmr/META-INF/MANIFEST.MF +++ b/addons/binding/org.openhab.binding.dsmr/META-INF/MANIFEST.MF @@ -15,16 +15,16 @@ Import-Package: org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.smarthome.config.core, org.eclipse.smarthome.config.discovery, - org.eclipse.smarthome.core.common, org.eclipse.smarthome.core.i18n, org.eclipse.smarthome.core.library.types, org.eclipse.smarthome.core.library.unit, org.eclipse.smarthome.core.thing, org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.type, org.eclipse.smarthome.core.types, + org.eclipse.smarthome.core.util, org.eclipse.smarthome.io.transport.serial, org.osgi.framework, - org.slf4j + org.slf4j, + tec.uom.se.format, + tec.uom.se.unit Service-Component: OSGI-INF/*.xml diff --git a/addons/binding/org.openhab.binding.dsmr/README.md b/addons/binding/org.openhab.binding.dsmr/README.md index 4cb7786517c1e..28c960918c806 100644 --- a/addons/binding/org.openhab.binding.dsmr/README.md +++ b/addons/binding/org.openhab.binding.dsmr/README.md @@ -1,13 +1,14 @@ # DSMR Binding -The DSMR-binding is targeted towards Dutch users having a smart meter (Dutch: 'Slimme meter'). -Data of Dutch smart meters can be obtained via the P1-port. +The DSMR-binding is targeted towards Dutch and Luxembourger users having a smart meter (Dutch: 'Slimme meter'). +Data of Dutch/Luxembourg smart meters can be obtained via the P1-port. When connecting this port from a serial port the data can be read out. This binding reads the P1-port of the Dutch Smart Meters that comply to NTA8130, DSMR v2.1, DSMR v2.2, DSMR v3.0, DSMR v4.0, DSMR v4.04, and DSMR 5.0. +This binding reads the P1-port of the Luxembourg’s electricity meter "Smarty" that comply to V1.0. Although DSMR v4.2 is not an official specification, the binding has support for this version. -If you are not living in the Netherlands but do want to read a meter please have look at the [SmartMeter Binding](https://www.openhab.org/addons/bindings/smartmeter). +If you are not living in the Netherlands/Luxembourg but do want to read a meter please have look at the [SmartMeter Binding](https://www.openhab.org/addons/bindings/smartmeter). Because the Dutch Meter standard is based on the IEC-62056-21 standard it might be desirable to build support for other country metering systems based on that standard in this binding. ## Serial Port Configuration @@ -16,6 +17,8 @@ The P1-port is a serial port. To configure the serial port within openHAB see th ## Supported Things +### dsmrBridge (The Netherlands) + `dsmrBridge`: This is the device that communicated between the binding (serial) and its internal meters. You always have to have a 'Dutch Smart Meter'-bridge. The bridge contains the serial port configuration. Specific meters are bound via the bridge to the smart meter. A smart meter consists typically out of minimal 2 meters. @@ -23,24 +26,43 @@ A generic meter and the electricity meter. Each meter is bound to the DSMR proto For each meter it is possible to set a refresh rate at which the status is updated. The physical meter might update with a high frequency per second, while it's desired to have only values per minute. +### smartyBridge (Luxembourg) + +`smartyBridge`: This is the device that communicated between the binding (serial) and its internal meters. +You always have to have a 'Dutch Smart Meter'-bridge. The bridge contains the serial port configuration. + ## Discovery -Both bridges and meters can be discovered via the discovery process. +The `dsmrBridge` and meters can be discovered via the discovery process. +The `smartyBridge` can be discovered. +Because the `smartyBridge` requires a decryption key. +You need to set the decryption key when the bridge is added. +After the decryption key is set a new discovery can be started to discover the meter. + If a bridge is manually configured it is possible to auto detect available meters. ### Configuration -The configuration for the Bridge consists of the following parameters: +The configuration for the `dsmrBridge` consists of the following parameters: | Parameter | Description | |---------------------|-------------------------------------------------------------------------------------------------------------| | serialPort | The serial port where the P1-port is connected to (e.g. Linux: `/dev/ttyUSB1`, Windows: `COM2`) (mandatory) | -| receivedTimeout | The time out period in which messages are expected to arrive, default is 120 seconds. | +| receivedTimeout | The time out period in which messages are expected to arrive, default is 120 seconds | | baudrate | Baudrate when no auto detect. valid values: 4800, 9600, 19200, 38400, 57600, 115200 | | databits | Data bits when no auto detect. valid values: 5, 6, 7, 8 | | parity | Parity when no auto detect. valid values: E(ven), N(one), O(dd) | | stopbits | Stop bits when no auto detect. valid values: 1, 1.5, 2 | +The configuration for the `smartyBridge` consists of the following parameters: + +| Parameter | Description | +|---------------------|-------------------------------------------------------------------------------------------------------------| +| serialPort | The serial port where the P1-port is connected to (e.g. Linux: `/dev/ttyUSB1`, Windows: `COM2`) (mandatory) | +| decryptionKey | The meter specific decryption key (mandatory) | +| receivedTimeout | The time out period in which messages are expected to arrive, default is 120 seconds | + + **Note:** *The manual configuration is only needed if the DSMR-device requires non DSMR-standard Serial Port parameters (i.e. something different then `115200 8N1` or `9600 7E1`)* ### Troubleshooting @@ -69,6 +91,7 @@ Supported meters: | DSMR V4.0.4 Electricity meter | `electricity_v4_0_4` | 0 | 10 seconds | | DSMR V4.2 Electricity meter | `electricity_v4_2` | 0 | 10 seconds | | DSMR V5 Electricity meter | `electricity_v5_0` | 0 | 10 seconds | +| Smarty V1.0 Electricity Meter | `electricity_smarty_v1_0` | 0 | 10 seconds | | ACE4000 GTMM Mk3 Gas meter | `gas_ace4000` | 3 | 1 hour | | DSMR V2.1 Gas meter | `gas_v2_1` | 0 | 24 hours | | DSMR V2.2 Gas meter | `gas_v2_2` | 0 | 24 hours | @@ -128,120 +151,131 @@ The following channels are supported: - O channel is supported only if the device has this functionality -| Channel Type ID | Item Type | Description | Ace4000 | DSMR V2.1 | DSMR V2.2 | DSMR V3.0 | DSMR V4.0 | DSMR V4.0.4 | DSMR V4.2 | DSMR V5 | -|--------------------------------------------------|-----------|------------------------------------------------------------------------|---------|-----------|-----------|-----------|-----------|-------------|-----------|---------| -| | | **Channels for the generic device** | | | | | | | | | -| `p1_text_code` | String | Text code from the device | - | Y | Y | Y | Y | Y | Y | - | -| `p1_text_string` | String | Text string from the device | - | Y | Y | Y | Y | Y | Y | Y | -| `p1_version_output` | String | Version information (most times this refers to the DSMR specification) | - | - | - | - | Y | Y | Y | Y | -| `p1_timestamp` | DateTime | Timestamp of the last device reading | - | - | - | - | Y | Y | Y | Y | -| | | **Channels for the cooling meter** | | | | | | | | | -| `cmeter_value_v2` | Number | The total amount of cooling used in the past period (GJ) | Y | - | Y | - | - | - | - | - | -| `cmeter_value_v2_timestamp` | DateTime | Timestamp of the last meter reading | Y | - | Y | - | - | - | - | - | -| `cmeter_equipment_identifier_v2_2` | String | Equipment identifier | - | - | Y | - | - | - | - | - | -| | | **Channels for the main electricity meter** | | | | | | | | | -| `emeter_equipment_identifier_v2_x` | String | Electricity Equipment identifier | - | Y | Y | - | - | - | - | - | -| `emeter_equipment_identifier` | String | Electricity Equipment identifier | - | - | - | Y | Y | Y | Y | Y | -| `emeter_delivery_tariff0` | Number | Total amount of electricity used for tariff 0 (kWh) | Y | - | - | - | - | - | - | - | -| `emeter_delivery_tariff1` | Number | Total amount of electricity used for tariff 1 (kWh) | Y | Y | Y | Y | Y | Y | Y | Y | -| `emeter_delivery_tariff2` | Number | Total amount of electricity used for tariff 2 (kWh) | Y | Y | Y | Y | Y | Y | Y | Y | -| `emeter_production_tariff0` | Number | Total amount of electricity produced for tariff 0 (kWh) | Y | - | - | - | - | - | - | - | -| `emeter_production_tariff1` | Number | Total amount of electricity produced for tariff 1 (kWh) | Y | Y | Y | Y | Y | Y | Y | Y | -| `emeter_production_tariff2` | Number | Total amount of electricity produced for tariff 2 (kWh) | Y | Y | Y | Y | Y | Y | Y | Y | -| `emeter_delivery_tariff0_antifraud` | Number | Total amount of electricity used for tariff 2 [antifraud] (kWh) | Y | - | - | - | - | - | - | - | -| `emeter_delivery_tariff1_antifraud` | Number | Total amount of electricity used for tariff 1 [antifraud] (kWh) | Y | - | - | - | - | - | - | - | -| `emeter_delivery_tariff2_antifraud` | Number | Total amount of electricity used for tariff 2 [antifraud] (kWh) | Y | - | - | - | - | - | - | - | -| `emeter_tariff_indicator` | String | Current tariff indicator | Y | Y | Y | Y | Y | Y | Y | Y | -| `emeter_treshold_a_v2_1` | Number | Actual treshold (A) | - | Y | - | - | - | - | - | - | -| `emeter_treshold_a` | Number | Actual treshold (A) | Y | - | Y | Y | - | - | - | - | -| `emeter_treshold_kwh` | Number | Actual treshold (kW) | - | - | - | - | Y | Y | - | - | -| `emeter_switch_position_v2_1` | Number | Switch position | - | Y | - | - | - | - | - | - | -| `emeter_switch_position` | Number | Switch position | Y | - | Y | Y | Y | Y | - | - | -| `emeter_active_import_power` | Number | Aggregate active import power (W) | Y | - | - | - | - | - | - | - | -| `emeter_actual_delivery` | Number | Current power delivery (kW) | - | Y | Y | Y | Y | Y | Y | Y | -| `emeter_actual_production` | Number | Current power production (kW) | - | - | - | Y | Y | Y | Y | Y | -| `emeter_power_failures` | Number | Number of power failures | - | - | - | - | Y | Y | Y | Y | -| `emeter_long_power_failures` | Number | Number of long power failures | - | - | - | - | Y | Y | Y | Y | -| `emeter_power_failure_log_entries` | Number | Number of entries in the power failure log | - | - | - | - | Y | Y | Y | Y | -| `emeter_power_failure_log_timestamp[x]` *note 2* | Number | Number of entries in the power failure log | - | - | - | - | Y | Y | Y | Y | -| `emeter_power_failure_log_duration[x]` *note 2* | Number | Number of entries in the power failure log | - | - | - | - | Y | Y | Y | Y | -| `emeter_voltage_sags_l1` | Number | Number of voltage sags L1 | - | - | - | - | Y | Y | Y | Y | -| `emeter_voltage_sags_l2` | Number | Number of voltage sags L2 | - | - | - | - | O | O | O | O | -| `emeter_voltage_sags_l3` | Number | Number of voltage sags L3 | - | - | - | - | O | O | O | O | -| `emeter_voltage_swells_l1` | Number | Number of voltage swells L1 | - | - | - | - | Y | Y | Y | Y | -| `emeter_voltage_swells_l2` | Number | Number of voltage swells L2 | - | - | - | - | O | O | O | O | -| `emeter_voltage_swells_l3` | Number | Number of voltage swells L3 | - | - | - | - | O | O | O | O | -| `emeter_instant_current_l1` | Number | Instant Current L1 (A) | - | - | - | - | Y | Y | Y | Y | -| `emeter_instant_current_l2` | Number | Instant Current L2 (A) | - | - | - | - | O | O | O | O | -| `emeter_instant_current_l3` | Number | Instant Current L3 (A) | - | - | - | - | O | O | O | O | -| `emeter_instant_power_delivery_l1` | Number | Instant Power Delivery L1 (kW) | - | - | - | - | Y | Y | Y | Y | -| `emeter_instant_power_delivery_l2` | Number | Instant Power Delivery L2 (kW) | - | - | - | - | O | O | O | O | -| `emeter_instant_power_delivery_l3` | Number | Instant Power Delivery L3 (kW) | - | - | - | - | O | O | O | O | -| `emeter_instant_power_production_l1` | Number | Instant Power Production L1 (kW) | - | - | - | - | Y | Y | Y | Y | -| `emeter_instant_power_production_l2` | Number | Instant Power Production L2 (kW) | - | - | - | - | O | O | O | O | -| `emeter_instant_power_production_l3` | Number | Instant Power Production L3 (kW) | - | - | - | - | O | O | O | O | -| `emeter_instant_voltage_l1` | Number | Instant Voltage L1 (V) | - | - | - | - | - | - | - | Y | -| `emeter_instant_voltage_l2` | Number | Instant Voltage L2 (V) | - | - | - | - | - | - | - | O | -| `emeter_instant_voltage_l3` | Number | Instant Voltage L3 (V) | - | - | - | - | - | - | - | O | -| | | **Channels for the slave electricity meter** | | | | | | | | | -| `meter_device_type` | String | Slave Electricity Meter Device Type | - | - | - | - | Y | Y | Y | Y | -| `meter_equipment_identifier` | String | Slave Electricity Meter ID | - | - | - | - | Y | Y | Y | Y | -| `emeter_delivery_tariff0` | Number | Total amount of slave electricity used for tariff 0 (kWh) | Y | - | - | - | - | - | - | - | -| `emeter_delivery_tariff1` | Number | Total amount of slave electricity used for tariff 1 (kWh) | Y | - | - | - | - | - | - | - | -| `emeter_delivery_tariff2` | Number | Total amount of slave electricity used for tariff 2 (kWh) | Y | - | - | - | - | - | - | - | -| `emeter_production_tariff0` | Number | Total amount of slave electricity produced for tariff 0 (kWh) | Y | - | - | - | - | - | - | - | -| `emeter_production_tariff1` | Number | Total amount of slave electricity produced for tariff 1 (kWh) | Y | - | - | - | - | - | - | - | -| `emeter_production_tariff2` | Number | Total amount of slave electricity produced for tariff 2 (kWh) | Y | - | - | - | - | - | - | - | -| `emeter_tariff_indicator` | String | Current slave tariff indicator | Y | - | - | - | - | - | - | - | -| `emeter_treshold_a` | Number | Actual slave treshold (A) | Y | - | - | - | - | - | - | - | -| `meter_switch_position` | Number | Slave electricity switch position | Y | - | - | Y | Y | Y | - | - | -| `emeter_active_import_power` | Number | Slave aggregate active import power (W) | Y | - | - | - | - | - | - | - | -| `emeter_value` | Number | Slave electricity usage (kWh) in the past period | - | - | - | - | Y | Y | Y | Y | -| `emeter_value_timestamp` | DateTime | Timestamp of the last reading | - | - | - | - | Y | Y | Y | Y | -| `meter_device_type` | String | Gas Meter Device Type | - | - | - | Y | - | - | - | - | -| `meter_equipment_identifier` | String | Gas Meter ID | Y | - | - | Y | - | - | - | - | -| | | **Channels for the gas meter** | | | | | | | | | -| `gmeter_equipment_identifier_v2` | String | Gas Meter ID | - | Y | Y | - | - | - | - | - | -| `gmeter_24h_delivery_v2` | Number | Gas Delivery past 24 hours | Y | Y | Y | - | - | - | - | - | -| `gmeter_24h_delivery_v2_timestamp` | DateTime | Timestamp of the last reading | Y | Y | Y | - | - | - | - | - | -| `gmeter_24h_delivery_compensated_v2` | Number | Gas Delivery past 24 hours (compensated) | - | Y | Y | - | - | - | - | - | -| `gmeter_24h_delivery_compensated_v2_timestamp` | DateTime | Timestamp of the last reading | - | Y | Y | - | - | - | - | - | -| `gmeter_value_v3` | Number | Gas Delivery past period | - | - | - | Y | - | - | - | - | -| `gmeter_value_v3_timestamp` | DateTime | Timestamp of the last reading | - | - | - | Y | - | - | - | - | -| `gmeter_valve_position_v2_1` | Number | Gas Valve position | - | Y | - | - | - | - | - | - | -| `gmeter_valve_position_v2_2` | Number | Gas Valve position | Y | - | Y | - | - | - | - | - | -| | | **Channels for the generic meter** | | | | | | | | | -| `meter_valve_switch_position` | Number | Gas Valve position | - | - | - | Y | - | - | - | - | -| `meter_device_type` | String | Generic Meter Device Type | - | - | - | Y | - | - | - | - | -| `gmeter_equipment_identifier` | String | Generic Meter ID | - | - | - | Y | - | - | - | - | -| `genmeter_value_v3` | Number | Delivery past period | - | - | - | Y | - | - | - | - | -| `meter_valve_switch_position` | Number | Generic Meter Valve/Switch position | - | - | - | Y | - | - | - | - | -| | | **Channels for the GJ meter (Heating or Cooling)** | | | | | | | | | -| `meter_device_type` | String | GJ Meter Device Type | - | - | - | Y | Y | Y | Y | Y | -| `meter_equipment_identifier` | Number | GJ Meter ID | - | - | - | Y | Y | Y | Y | Y | -| `gjmeter_value_v3` | Number | GJ Delivery past period | - | - | - | Y | - | - | - | - | -| `gjmeter_value_v3_timestamp` | DateTime | Timestamp of the last reading | - | - | - | Y | - | - | - | - | -| `gjmeter_value_v4` | Number | GJ Delivery past period | - | - | - | - | Y | Y | Y | Y | -| `gjmeter_value_v4_timestamp` | DateTime | Timestamp of the last reading | - | - | - | - | Y | Y | Y | Y | -| | | **Channels for the heating meter** | | | | | | | | | -| `meter_valve_switch_position` | Number | GJ Meter Valve position | - | - | - | Y | Y | Y | Y | - | -| `meter_equipment_identifier` | String | Heating Meter ID | Y | - | - | - | - | - | - | - | -| `hmeter_equipment_identifier_v2_2` | String | Heating Meter ID | - | - | Y | - | - | - | - | - | -| `hmeter_value_v2` | Number | Heating Delivery past period | Y | - | Y | - | - | - | - | - | -| `hmeter_value_v2_timestamp` | DateTime | Timestamp of the last reading | Y | - | Y | - | - | - | - | - | -| | | **Channels for the m3 meter (Gas or Water)** | | | | | | | | | -| `meter_device_type` | String | m3 Meter Device Type | - | - | - | - | Y | Y | Y | Y | -| `meter_equipment_identifier` | String | m3 Meter ID | - | - | - | - | Y | Y | Y | Y | -| `m3meter_value` | Number | m3 Delivery past period | - | - | - | - | Y | Y | Y | Y | -| `meter_valve_switch_position` | Number | m3 Meter Valve position | - | - | - | - | Y | Y | Y | - | -| | | **Channels for the water meter** | | | | | | | | | -| `meter_device_type` | String | Water Meter Device Type | - | - | - | Y | - | - | - | - | -| `meter_equipment_identifier` | String | Water Meter ID | Y | - | - | Y | - | - | - | - | -| `wmeter_equipment_identifier_v2_2` | String | Water Meter ID | - | - | Y | - | - | - | - | - | -| `wmeter_value_v2` | Number | Water Delivery past period | Y | - | Y | - | - | - | - | - | -| `wmeter_value_v2_timestamp` | DateTime | Timestamp of the last reading | Y | - | Y | - | - | - | - | - | -| `wmeter_value_v3` | Number | Water Delivery past period | - | - | - | Y | - | - | - | - | -| `meter_valve_switch_position` | Number | Water Meter Valve position | - | - | - | Y | - | - | - | - | +| Channel Type ID | Item Type | Description | Ace4000 | DSMR V2.1 | DSMR V2.2 | DSMR V3.0 | DSMR V4.0 | DSMR V4.0.4 | DSMR V4.2 | DSMR V5 | SMARTY V1.0 | +|--------------------------------------------------|--------------------------|------------------------------------------------------------------------|---------|-----------|-----------|-----------|-----------|-------------|-----------|---------|-------------| +| | | **Channels for the generic device** | | | | | | | | | | +| `p1_text_code` | String | Text code from the device | - | Y | Y | Y | Y | Y | Y | - | - | +| `p1_text_string` | String | Text string from the device | - | Y | Y | Y | Y | Y | Y | Y | Y | +| `p1_version_output` | String | Version information (most times this refers to the DSMR specification) | - | - | - | - | Y | Y | Y | Y | Y | +| `p1_timestamp` | DateTime | Timestamp of the last device reading | - | - | - | - | Y | Y | Y | Y | Y | +| | | **Channels for the cooling meter** | | | | | | | | | | +| `cmeter_value_v2` | Number:Energy | The total amount of cooling used in the past period (GJ) | Y | - | Y | - | - | - | - | - | - | +| `cmeter_value_v2_timestamp` | DateTime | Timestamp of the last meter reading | Y | - | Y | - | - | - | - | - | - | +| `cmeter_equipment_identifier_v2_2` | String | Equipment identifier | - | - | Y | - | - | - | - | - | - | +| | | **Channels for the main electricity meter** | | | | | | | | | | +| `emeter_equipment_identifier_v2_x` | String | Electricity Equipment identifier | - | Y | Y | - | - | - | - | - | Y | +| `emeter_equipment_identifier` | String | Electricity Equipment identifier | - | - | - | Y | Y | Y | Y | Y | - | +| `emeter_delivery_tariff0` | Number:Energy | Total amount of electricity used for tariff 0 (kWh) | Y | - | - | - | - | - | - | - | Y | +| `emeter_delivery_tariff1` | Number:Energy | Total amount of electricity used for tariff 1 (kWh) | Y | Y | Y | Y | Y | Y | Y | Y | - | +| `emeter_delivery_tariff2` | Number:Energy | Total amount of electricity used for tariff 2 (kWh) | Y | Y | Y | Y | Y | Y | Y | Y | - | +| `emeter_production_tariff0` | Number:Energy | Total amount of electricity produced for tariff 0 (kWh) | Y | - | - | - | - | - | - | - | Y | +| `emeter_production_tariff1` | Number:Energy | Total amount of electricity produced for tariff 1 (kWh) | Y | Y | Y | Y | Y | Y | Y | Y | - | +| `emeter_production_tariff2` | Number:Energy | Total amount of electricity produced for tariff 2 (kWh) | Y | Y | Y | Y | Y | Y | Y | Y | - | +| `emeter_delivery_tariff0_antifraud` | Number:Energy | Total amount of electricity used for tariff 2 [antifraud] (kWh) | Y | - | - | - | - | - | - | - | - | +| `emeter_delivery_tariff1_antifraud` | Number:Energy | Total amount of electricity used for tariff 1 [antifraud] (kWh) | Y | - | - | - | - | - | - | - | - | +| `emeter_delivery_tariff2_antifraud` | Number:Energy | Total amount of electricity used for tariff 2 [antifraud] (kWh) | Y | - | - | - | - | - | - | - | - | +| `emeter_total_imported_energy_register_q` | Number:Energy | Total Imported Energy (Q+) (kvarh) | - | - | - | - | - | - | - | - | Y | +| `emeter_total_exported_energy_register_q` | Number:Energy | Total Exported Energy (Q-) (kvarh) | - | - | - | - | - | - | - | - | Y | +| `emeter_tariff_indicator` | String | Current tariff indicator | Y | Y | Y | Y | Y | Y | Y | Y | - | +| `emeter_treshold_a_v2_1` | Number:ElectricCurrent | Actual treshold (A) | - | Y | - | - | - | - | - | - | - | +| `emeter_treshold_a` | Number:ElectricCurrent | Actual treshold (A) | Y | - | Y | Y | - | - | - | - | - | +| `emeter_treshold_kwh` | Number:Power | Actual treshold (kW) | - | - | - | - | Y | Y | - | - | - | +| `emeter_switch_position_v2_1` | Number | Switch position | - | Y | - | - | - | - | - | - | - | +| `emeter_switch_position` | Number | Switch position | Y | - | Y | Y | Y | Y | - | - | Y | +| `emeter_active_import_power` | Number:Power | Aggregate active import power (W) | Y | - | - | - | - | - | - | - | - | +| `emeter_actual_delivery` | Number:Power | Current power delivery (kW) | - | Y | Y | Y | Y | Y | Y | Y | Y | +| `emeter_actual_production` | Number:Power | Current power production (kW) | - | - | - | Y | Y | Y | Y | Y | Y | +| `emeter_actual_reactive_delivery` | Number | Actual Reactive Power Delivery (kvar) | - | - | - | - | - | - | - | - | Y | +| `emeter_actual_reactive_production` | Number | Actual Reactive Power Production (kvar) | - | - | - | - | - | - | - | - | Y | +| `emeter_active_threshold_smax` | Number | Active threshold (SMAX) (kVA) | - | - | - | - | - | - | - | - | Y | +| `emeter_power_failures` | Number | Number of power failures | - | - | - | - | Y | Y | Y | Y | Y | +| `emeter_long_power_failures` | Number | Number of long power failures | - | - | - | - | Y | Y | Y | Y | - | +| `emeter_power_failure_log_entries` | Number | Number of entries in the power failure log | - | - | - | - | Y | Y | Y | Y | Y | +| `emeter_power_failure_log_timestamp[x]` *note 2* | DateTime | Timestamp for entry [x] in the power failure log | - | - | - | - | Y | Y | Y | Y | Y | +| `emeter_power_failure_log_duration[x]` *note 2* | Number:Time | Duration for entry [x] the power failure log | - | - | - | - | Y | Y | Y | Y | Y | +| `emeter_voltage_sags_l1` | Number | Number of voltage sags L1 | - | - | - | - | Y | Y | Y | Y | Y | +| `emeter_voltage_sags_l2` | Number | Number of voltage sags L2 | - | - | - | - | O | O | O | O | O | +| `emeter_voltage_sags_l3` | Number | Number of voltage sags L3 | - | - | - | - | O | O | O | O | O | +| `emeter_voltage_swells_l1` | Number | Number of voltage swells L1 | - | - | - | - | Y | Y | Y | Y | - | +| `emeter_voltage_swells_l2` | Number | Number of voltage swells L2 | - | - | - | - | O | O | O | O | - | +| `emeter_voltage_swells_l3` | Number | Number of voltage swells L3 | - | - | - | - | O | O | O | O | - | +| `emeter_instant_current_l1` | Number:ElectricCurrent | Instant Current L1 (A) | - | - | - | - | Y | Y | Y | Y | Y | +| `emeter_instant_current_l2` | Number:ElectricCurrent | Instant Current L2 (A) | - | - | - | - | O | O | O | O | O | +| `emeter_instant_current_l3` | Number:ElectricCurrent | Instant Current L3 (A) | - | - | - | - | O | O | O | O | O | +| `emeter_instant_power_delivery_l1` | Number:Power | Instant Power Delivery L1 (kW) | - | - | - | - | Y | Y | Y | Y | Y | +| `emeter_instant_power_delivery_l2` | Number:Power | Instant Power Delivery L2 (kW) | - | - | - | - | O | O | O | O | O | +| `emeter_instant_power_delivery_l3` | Number:Power | Instant Power Delivery L3 (kW) | - | - | - | - | O | O | O | O | O | +| `emeter_instant_power_production_l1` | Number:Power | Instant Power Production L1 (kW) | - | - | - | - | Y | Y | Y | Y | Y | +| `emeter_instant_power_production_l2` | Number:Power | Instant Power Production L2 (kW) | - | - | - | - | O | O | O | O | O | +| `emeter_instant_power_production_l3` | Number:Power | Instant Power Production L3 (kW) | - | - | - | - | O | O | O | O | O | +| `emeter_instant_reactive_power_delivery_l1` | Number:Power | Instant Reactive Power Delivery L1 (kvar) | - | - | - | - | - | - | - | - | Y | +| `emeter_instant_reactive_power_delivery_l2` | Number:Power | Instant Reactive Power Delivery L2 (kvar) | - | - | - | - | - | - | - | - | Y | +| `emeter_instant_reactive_power_delivery_l3` | Number:Power | Instant Reactive Power Delivery L3 (kvar) | - | - | - | - | - | - | - | - | Y | +| `emeter_instant_reactive_power_production_l1` | Number:Power | Instant Reactive Power Prodcution L1 (kvar) | - | - | - | - | - | - | - | - | Y | +| `emeter_instant_reactive_power_production_l2` | Number:Power | Instant Reactive Power Prodcution L2 (kvar) | - | - | - | - | - | - | - | - | Y | +| `emeter_instant_reactive_power_production_l3` | Number:Power | Instant Reactive Power Prodcution L3 (kvar) | - | - | - | - | - | - | - | - | Y | +| `emeter_instant_voltage_l1` | Number:ElectricPotential | Instant Voltage L1 (V) | - | - | - | - | - | - | - | Y | - | +| `emeter_instant_voltage_l2` | Number:ElectricPotential | Instant Voltage L2 (V) | - | - | - | - | - | - | - | O | - | +| `emeter_instant_voltage_l3` | Number:ElectricPotential | Instant Voltage L3 (V) | - | - | - | - | - | - | - | O | - | +| | | **Channels for the slave electricity meter** | | | | | | | | | | +| `meter_device_type` | String | Slave Electricity Meter Device Type | - | - | - | - | Y | Y | Y | Y | - | +| `meter_equipment_identifier` | String | Slave Electricity Meter ID | - | - | - | - | Y | Y | Y | Y | - | +| `emeter_delivery_tariff0` | Number:Energy | Total amount of slave electricity used for tariff 0 (kWh) | Y | - | - | - | - | - | - | - | - | +| `emeter_delivery_tariff1` | Number:Energy | Total amount of slave electricity used for tariff 1 (kWh) | Y | - | - | - | - | - | - | - | - | +| `emeter_delivery_tariff2` | Number:Energy | Total amount of slave electricity used for tariff 2 (kWh) | Y | - | - | - | - | - | - | - | - | +| `emeter_production_tariff0` | Number:Energy | Total amount of slave electricity produced for tariff 0 (kWh) | Y | - | - | - | - | - | - | - | - | +| `emeter_production_tariff1` | Number:Energy | Total amount of slave electricity produced for tariff 1 (kWh) | Y | - | - | - | - | - | - | - | - | +| `emeter_production_tariff2` | Number:Energy | Total amount of slave electricity produced for tariff 2 (kWh) | Y | - | - | - | - | - | - | - | - | +| `emeter_tariff_indicator` | String | Current slave tariff indicator | Y | - | - | - | - | - | - | - | - | +| `emeter_treshold_a` | Number:ElectricCurrent | Actual slave treshold (A) | Y | - | - | - | - | - | - | - | - | +| `meter_switch_position` | Number | Slave electricity switch position | Y | - | - | Y | Y | Y | - | - | - | +| `emeter_active_import_power` | Number:Power | Slave aggregate active import power (W) | Y | - | - | - | - | - | - | - | - | +| `emeter_value` | Number:Energy | Slave electricity usage (kWh) in the past period | - | - | - | - | Y | Y | Y | Y | - | +| `emeter_value_timestamp` | DateTime | Timestamp of the last reading | - | - | - | - | Y | Y | Y | Y | - | +| `meter_device_type` | String | Gas Meter Device Type | - | - | - | Y | - | - | - | - | - | +| `meter_equipment_identifier` | String | Gas Meter ID | Y | - | - | Y | - | - | - | - | - | +| | | **Channels for the gas meter** | | | | | | | | | | +| `gmeter_equipment_identifier_v2` | String | Gas Meter ID | - | Y | Y | - | - | - | - | - | - | +| `gmeter_24h_delivery_v2` | Number:Volume | Gas Delivery past 24 hours | Y | Y | Y | - | - | - | - | - | - | +| `gmeter_24h_delivery_v2_timestamp` | DateTime | Timestamp of the last reading | Y | Y | Y | - | - | - | - | - | - | +| `gmeter_24h_delivery_compensated_v2` | Number:Volume | Gas Delivery past 24 hours (compensated) | - | Y | Y | - | - | - | - | - | - | +| `gmeter_24h_delivery_compensated_v2_timestamp` | DateTime | Timestamp of the last reading | - | Y | Y | - | - | - | - | - | - | +| `gmeter_value_v3` | Number:Volume | Gas Delivery past period | - | - | - | Y | - | - | - | - | - | +| `gmeter_value_v3_timestamp` | DateTime | Timestamp of the last reading | - | - | - | Y | - | - | - | - | - | +| `gmeter_valve_position_v2_1` | Number | Gas Valve position | - | Y | - | - | - | - | - | - | - | +| `gmeter_valve_position_v2_2` | Number | Gas Valve position | Y | - | Y | - | - | - | - | - | - | +| | | **Channels for the generic meter** | | | | | | | | | | +| `meter_valve_switch_position` | Number | Gas Valve position | - | - | - | Y | - | - | - | - | - | +| `meter_device_type` | String | Generic Meter Device Type | - | - | - | Y | - | - | - | - | - | +| `gmeter_equipment_identifier` | String | Generic Meter ID | - | - | - | Y | - | - | - | - | - | +| `genmeter_value_v3` | Number | Delivery past period | - | - | - | Y | - | - | - | - | - | +| `meter_valve_switch_position` | Number | Generic Meter Valve/Switch position | - | - | - | Y | - | - | - | - | - | +| | | **Channels for the GJ meter (Heating or Cooling)** | | | | | | | | | | +| `meter_device_type` | String | GJ Meter Device Type | - | - | - | Y | Y | Y | Y | Y | - | +| `meter_equipment_identifier` | Number | GJ Meter ID | - | - | - | Y | Y | Y | Y | Y | - | +| `gjmeter_value_v3` | Number:Energy | GJ Delivery past period | - | - | - | Y | - | - | - | - | - | +| `gjmeter_value_v3_timestamp` | DateTime | Timestamp of the last reading | - | - | - | Y | - | - | - | - | - | +| `gjmeter_value_v4` | Number:Energy | GJ Delivery past period | - | - | - | - | Y | Y | Y | Y | - | +| `gjmeter_value_v4_timestamp` | DateTime | Timestamp of the last reading | - | - | - | - | Y | Y | Y | Y | - | +| | | **Channels for the heating meter** | | | | | | | | | | +| `meter_valve_switch_position` | Number | GJ Meter Valve position | - | - | - | Y | Y | Y | Y | - | - | +| `meter_equipment_identifier` | String | Heating Meter ID | Y | - | - | - | - | - | - | - | - | +| `hmeter_equipment_identifier_v2_2` | String | Heating Meter ID | - | - | Y | - | - | - | - | - | - | +| `hmeter_value_v2` | Number:Energy | Heating Delivery past period | Y | - | Y | - | - | - | - | - | - | +| `hmeter_value_v2_timestamp` | DateTime | Timestamp of the last reading | Y | - | Y | - | - | - | - | - | - | +| | | **Channels for the m3 meter (Gas or Water)** | | | | | | | | | | +| `meter_device_type` | String | m3 Meter Device Type | - | - | - | - | Y | Y | Y | Y | - | +| `meter_equipment_identifier` | String | m3 Meter ID | - | - | - | - | Y | Y | Y | Y | - | +| `m3meter_value` | Number:Volume | m3 Delivery past period | - | - | - | - | Y | Y | Y | Y | - | +| `meter_valve_switch_position` | Number | m3 Meter Valve position | - | - | - | - | Y | Y | Y | - | - | +| | | **Channels for the water meter** | | | | | | | | | | +| `meter_device_type` | String | Water Meter Device Type | - | - | - | Y | - | - | - | - | - | +| `meter_equipment_identifier` | String | Water Meter ID | Y | - | - | Y | - | - | - | - | - | +| `wmeter_equipment_identifier_v2_2` | String | Water Meter ID | - | - | Y | - | - | - | - | - | - | +| `wmeter_value_v2` | Number:Volume | Water Delivery past period | Y | - | Y | - | - | - | - | - | - | +| `wmeter_value_v2_timestamp` | DateTime | Timestamp of the last reading | Y | - | Y | - | - | - | - | - | - | +| `wmeter_value_v3` | Number:Volume | Water Delivery past period | - | - | - | Y | - | - | - | - | - | +| `meter_valve_switch_position` | Number | Water Meter Valve position | - | - | - | Y | - | - | - | - | - | *note 2*. The power failure log has a dynamic number of entries starting at `0`. So `emeter_power_failure_log_timestamp0`, `emeter_power_failure_log_duration0` refers to the first entry, @@ -263,7 +297,7 @@ ItemType "" () {channel=""} **Examples** ``` -Number MeterDeliveryTariff0 "Total electricity delivered to the resident during low tariff period [%.3f kWh]" {channel="dsmr:electricity_v5_0:mysmartmeter:electricityV5:emeter_delivery_tariff1} +Number:Energy MeterDeliveryTariff0 "Total electricity delivered to the resident during low tariff period [%.3f kWh]" {channel="dsmr:electricity_v5_0:mysmartmeter:electricityV5:emeter_delivery_tariff1} ``` ## Full configuration example @@ -282,8 +316,8 @@ Bridge dsmr:dsmrBridge:mysmartmeter [serialPort="/dev/ttyUSB0"] { ``` String P1Version "P1 Version output" {channel="dsmr:device_v5:mysmartmeter:dsmrV5Device:p1_version_output"} -Number MeterDeliveryTariff0 "Total electricity delivered to the resident during low tariff period [%.3f kWh]" {channel="dsmr:device_v5:mysmartmeter:electricityV5:emeter_delivery_tariff1} -Number MeterDeliveryTariff1 "Total electricity delivered to the resident during high tariff period [%.3f kWh]" {channel="dsmr:device_v5:mysmartmeter:electricityV5:emeter_delivery_tariff2} +Number:Energy MeterDeliveryTariff0 "Total electricity delivered to the resident during low tariff period [%.3f kWh]" {channel="dsmr:device_v5:mysmartmeter:electricityV5:emeter_delivery_tariff1} +Number:Energy MeterDeliveryTariff1 "Total electricity delivered to the resident during high tariff period [%.3f kWh]" {channel="dsmr:device_v5:mysmartmeter:electricityV5:emeter_delivery_tariff2} ``` ## Determine M-Bus channel diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/DSMRBindingConstants.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/DSMRBindingConstants.java index ffcd350fa8bd2..6c1160fdd3f02 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/DSMRBindingConstants.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/DSMRBindingConstants.java @@ -34,9 +34,18 @@ public final class DSMRBindingConstants { public static final String DSMR_PORT_NAME = "org.openhab.binding.dsmr"; /** - * Bridge device thing + * Bridge device things */ public static final ThingTypeUID THING_TYPE_DSMR_BRIDGE = new ThingTypeUID(BINDING_ID, "dsmrBridge"); + public static final ThingTypeUID THING_TYPE_SMARTY_BRIDGE = new ThingTypeUID(BINDING_ID, "smartyBridge"); + + /** + * Configuration parameter for the serial port. + */ + public static final String CONFIGURATION_SERIAL_PORT = "serialPort"; + + public static final String CONFIGURATION_DECRYPTION_KEY = "decryptionKey"; + public static final String CONFIGURATION_DECRYPTION_KEY_EMPTY = ""; private DSMRBindingConstants() { // Constants class diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/DSMRHandlerFactory.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/DSMRHandlerFactory.java index d33d2dcc74d54..d00799f9b104b 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/DSMRHandlerFactory.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/DSMRHandlerFactory.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.dsmr.internal; +import static org.openhab.binding.dsmr.internal.DSMRBindingConstants.*; + import java.util.HashMap; import java.util.Hashtable; import java.util.Map; @@ -68,7 +70,7 @@ public class DSMRHandlerFactory extends BaseThingHandlerFactory { */ @Override public boolean supportsThingType(ThingTypeUID thingTypeUID) { - if (thingTypeUID.equals(DSMRBindingConstants.THING_TYPE_DSMR_BRIDGE)) { + if (THING_TYPE_DSMR_BRIDGE.equals(thingTypeUID) || THING_TYPE_SMARTY_BRIDGE.equals(thingTypeUID)) { logger.debug("DSMR Bridge Thing {} supported", thingTypeUID); return true; } else { @@ -97,7 +99,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); logger.debug("Searching for thingTypeUID {}", thingTypeUID); - if (DSMRBindingConstants.THING_TYPE_DSMR_BRIDGE.equals(thingTypeUID)) { + if (THING_TYPE_DSMR_BRIDGE.equals(thingTypeUID) || THING_TYPE_SMARTY_BRIDGE.equals(thingTypeUID)) { DSMRBridgeHandler handler = new DSMRBridgeHandler((Bridge) thing, serialPortManager); registerDiscoveryService(handler); return handler; diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDevice.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDevice.java index 2440b3e831ab6..ed0ee99a02c08 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDevice.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDevice.java @@ -12,11 +12,14 @@ */ package org.openhab.binding.dsmr.internal.device; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * Interface for classes controlling DSMR devices. * * @author Hilbrand Bouwkamp - Initial contribution */ +@NonNullByDefault public interface DSMRDevice { /** diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceConfiguration.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceConfiguration.java index 0ef8fe53d915c..034ef1b042841 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceConfiguration.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceConfiguration.java @@ -46,6 +46,11 @@ public class DSMRDeviceConfiguration { */ public String stopbits; + /** + * The Luxembourgish smart meter decryption key + */ + public String decryptionKey; + /** * When no message was received after the configured number of seconds action will be taken. */ @@ -61,6 +66,7 @@ public boolean isSerialFixedSettings() { @Override public String toString() { return "DSMRDeviceConfiguration [serialPort=" + serialPort + ", Baudrate=" + baudrate + ", Databits=" + databits - + ", Parity=" + parity + ", Stopbits=" + stopbits + ", receivedTimeout=" + receivedTimeout + "]"; + + ", Parity=" + parity + ", Stopbits=" + stopbits + ", decryptionKey=" + decryptionKey + + ", receivedTimeout=" + receivedTimeout + "]"; } } diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceRunnable.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceRunnable.java index c67f6702d9a9d..218ee4f46fbc0 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceRunnable.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceRunnable.java @@ -84,7 +84,7 @@ public void run() { } logger.trace("Device shutdown"); } catch (RuntimeException e) { - logger.warn("DSMRDeviceThread stopped with a RuntimeException", e); + logger.warn("DSMRDeviceRunnable stopped with a RuntimeException", e); portEventListener.handleErrorEvent(DSMRConnectorErrorEvent.READ_ERROR); } catch (InterruptedException e) { Thread.currentThread().interrupt(); diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRFixedConfigDevice.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRFixedConfigDevice.java index 3651eaf683453..4b1278b218bf8 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRFixedConfigDevice.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRFixedConfigDevice.java @@ -31,17 +31,20 @@ public class DSMRFixedConfigDevice implements DSMRDevice { private final DSMRTelegramListener telegramListener; /** - * Constructor + * Constructor. * * @param serialPortManager the manager to get a new serial port connecting from * @param serialPortName the port name (e.g. /dev/ttyUSB0 or COM1) * @param fixedPortSettings The serial port connection settings * @param listener the parent {@link DSMREventListener} + * @param telegramListener listener to report found telegrams or errors */ public DSMRFixedConfigDevice(SerialPortManager serialPortManager, String serialPortName, - DSMRSerialSettings fixedPortSettings, DSMREventListener listener) { + DSMRSerialSettings fixedPortSettings, DSMREventListener listener, DSMRTelegramListener telegramListener) { this.fixedPortSettings = fixedPortSettings; - telegramListener = new DSMRTelegramListener(listener); + this.telegramListener = telegramListener; + telegramListener.setDsmrEventListener(listener); + dsmrPort = new DSMRSerialConnector(serialPortManager, serialPortName, telegramListener); } diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDevice.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDevice.java index 48c47536080ba..f2b4c47aa8921 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDevice.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDevice.java @@ -64,7 +64,7 @@ enum DeviceState { * This factor is multiplied with the {@link #baudrateSwitchTimeoutSeconds} and used as the duration the discovery * of the baudrate may take. */ - private static final int DISCOVER_TIMEOUT_FACTOR = 4; + private static final long DISCOVER_TIMEOUT_FACTOR = 4L; /* * Februari 2017 @@ -127,16 +127,19 @@ enum DeviceState { * @param serialPortManager the manager to get a new serial port connecting from * @param serialPortName the port name (e.g. /dev/ttyUSB0 or COM1) * @param listener the parent {@link DSMREventListener} + * @param telegramListener listener to report found telegrams or errors * @param scheduler the scheduler to use with the baudrate switching timers * @param baudrateSwitchTimeoutSeconds timeout period for when to try other baudrate settings and end the discovery * of the baudrate */ public DSMRSerialAutoDevice(SerialPortManager serialPortManager, String serialPortName, DSMREventListener listener, - ScheduledExecutorService scheduler, int baudrateSwitchTimeoutSeconds) { + DSMRTelegramListener telegramListener, ScheduledExecutorService scheduler, + int baudrateSwitchTimeoutSeconds) { this.parentListener = listener; this.scheduler = scheduler; this.baudrateSwitchTimeoutSeconds = baudrateSwitchTimeoutSeconds; - telegramListener = new DSMRTelegramListener(listener); + this.telegramListener = telegramListener; + telegramListener.setDsmrEventListener(listener); dsmrConnector = new DSMRSerialConnector(serialPortManager, serialPortName, telegramListener); logger.debug("Initialized port '{}'", serialPortName); } diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRTelegramListener.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRTelegramListener.java index a2da26fca8f88..6c54535043d1c 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRTelegramListener.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRTelegramListener.java @@ -23,6 +23,7 @@ import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram.TelegramState; import org.openhab.binding.dsmr.internal.device.p1telegram.P1TelegramListener; import org.openhab.binding.dsmr.internal.device.p1telegram.P1TelegramParser; +import org.openhab.binding.dsmr.internal.device.p1telegram.TelegramParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,23 +35,31 @@ * @author Hilbrand Bouwkamp - Moved this code out of the DSMRPort class, fixed some issues and reduced code */ @NonNullByDefault -class DSMRTelegramListener implements P1TelegramListener, DSMRConnectorListener { +public class DSMRTelegramListener implements P1TelegramListener, DSMRConnectorListener { private final Logger logger = LoggerFactory.getLogger(DSMRTelegramListener.class); - private final P1TelegramParser parser; + private final TelegramParser parser; - private DSMREventListener dsmrEventListener; + private @NonNullByDefault({}) DSMREventListener dsmrEventListener; /** * Constructor. * * @param eventListener listener to send received errors or messages to */ - public DSMRTelegramListener(DSMREventListener eventListener) { - dsmrEventListener = eventListener; + public DSMRTelegramListener() { parser = new P1TelegramParser(this); } + /** + * Constructs {@link DSMRTelegramListener} with a Smarty decryptor to first decrypt incoming messages. + * + * @param decryptionKey Smarty decryption key + */ + public DSMRTelegramListener(String decryptionKey) { + parser = new SmartyDecrypter(new P1TelegramParser(this), this, decryptionKey); + } + /** * Set the DSMR event listener. * @@ -62,7 +71,7 @@ public void setDsmrEventListener(DSMREventListener eventListener) { @Override public void handleData(byte[] data, int length) { - parser.parseData(data, 0, length); + parser.parse(data, length); } @Override @@ -74,18 +83,17 @@ public void handleErrorEvent(DSMRConnectorErrorEvent portEvent) { /** * Handler for cosemObjects received in a P1 telegram * - * @param cosemObjects. List of received {@link CosemObject} objects - * @param telegramState. {@link TelegramState} describing the state of the received telegram. + * @param telegram the received telegram. */ @Override public void telegramReceived(P1Telegram telegram) { - TelegramState telegramState = telegram.getTelegramState(); - List cosemObjects = telegram.getCosemObjects(); + final TelegramState telegramState = telegram.getTelegramState(); + final List cosemObjects = telegram.getCosemObjects(); + if (logger.isTraceEnabled()) { logger.trace("Received {} Cosem Objects with state: '{}'", cosemObjects.size(), telegramState); } - - if (telegramState == TelegramState.OK) { + if (telegramState == TelegramState.OK || telegramState == TelegramState.INVALID_ENCRYPTION_KEY) { dsmrEventListener.handleTelegramReceived(telegram); } else { if (logger.isDebugEnabled()) { diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/SmartyDecrypter.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/SmartyDecrypter.java new file mode 100644 index 0000000000000..f161421f09581 --- /dev/null +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/SmartyDecrypter.java @@ -0,0 +1,232 @@ +/** + * Copyright (c) 2010-2019 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.dsmr.internal.device; + +import java.nio.ByteBuffer; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Collections; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.util.HexUtils; +import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram; +import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram.TelegramState; +import org.openhab.binding.dsmr.internal.device.p1telegram.P1TelegramListener; +import org.openhab.binding.dsmr.internal.device.p1telegram.TelegramParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Decodes messages send by The Luxembourgian Smart Meter "Smarty". + * + * @author Hilbrand Bouwkamp - Initial contribution + */ +@NonNullByDefault +public class SmartyDecrypter implements TelegramParser { + + private enum State { + WAITING_FOR_START_BYTE, + READ_SYSTEM_TITLE_LENGTH, + READ_SYSTEM_TITLE, + READ_SEPARATOR_82, + READ_PAYLOAD_LENGTH, + READ_SEPARATOR_30, + READ_FRAME_COUNTER, + READ_PAYLOAD, + READ_GCM_TAG, + DONE_READING_TELEGRAM; + } + + private static final byte START_BYTE = (byte) 0xDB; + private static final byte SEPARATOR_82 = (byte) 0x82; + private static final byte SEPARATOR_30 = 0x30; + private static final int ADD_LENGTH = 17; + private static final String ADD = "3000112233445566778899AABBCCDDEEFF"; + private static final byte[] ADD_DECODED = HexUtils.hexToBytes(ADD); + private static final int GCM_TAG_LENGTH = 12; + private static final int GCM_BITS = GCM_TAG_LENGTH * Byte.SIZE; + private static final int MESSAGES_BUFFER_SIZE = 4096; + + private final Logger logger = LoggerFactory.getLogger(SmartyDecrypter.class); + private final ByteBuffer iv = ByteBuffer.allocate(GCM_TAG_LENGTH); + private final ByteBuffer cipherText = ByteBuffer.allocate(MESSAGES_BUFFER_SIZE); + private final TelegramParser parser; + private @Nullable final SecretKeySpec secretKeySpec; + + private State state = State.WAITING_FOR_START_BYTE; + private int currentBytePosition; + private int changeToNextStateAt; + private int dataLength; + private boolean lenientMode; + private P1TelegramListener telegramListener; + + /** + * Constructor. + * + * @param parser parser of the Cosem messages + * @param telegramListener + * @param decryptionKey The key to decrypt the messages + */ + public SmartyDecrypter(TelegramParser parser, P1TelegramListener telegramListener, String decryptionKey) { + this.parser = parser; + this.telegramListener = telegramListener; + secretKeySpec = decryptionKey.isEmpty() ? null : new SecretKeySpec(HexUtils.hexToBytes(decryptionKey), "AES"); + } + + @Override + public void parse(byte[] data, int length) { + for (int i = 0; i < length; i++) { + currentBytePosition++; + if (processStateActions(data[i])) { + processCompleted(); + } + } + if (lenientMode && secretKeySpec == null) { + parser.parse(data, length); + } + } + + private boolean processStateActions(byte rawInput) { + switch (state) { + case WAITING_FOR_START_BYTE: + if (rawInput == START_BYTE) { + reset(); + state = State.READ_SYSTEM_TITLE_LENGTH; + } + break; + case READ_SYSTEM_TITLE_LENGTH: + state = State.READ_SYSTEM_TITLE; + // 2 start bytes (position 0 and 1) + system title length + changeToNextStateAt = 1 + rawInput; + break; + case READ_SYSTEM_TITLE: + iv.put(rawInput); + if (currentBytePosition >= changeToNextStateAt) { + state = State.READ_SEPARATOR_82; + changeToNextStateAt++; + } + break; + case READ_SEPARATOR_82: + if (rawInput == SEPARATOR_82) { + state = State.READ_PAYLOAD_LENGTH; // Ignore separator byte + changeToNextStateAt += 2; + } else { + logger.debug("Missing separator (0x82). Dropping telegram."); + state = State.WAITING_FOR_START_BYTE; + } + break; + case READ_PAYLOAD_LENGTH: + dataLength <<= 8; + dataLength |= rawInput; + if (currentBytePosition >= changeToNextStateAt) { + state = State.READ_SEPARATOR_30; + changeToNextStateAt++; + } + break; + case READ_SEPARATOR_30: + if (rawInput == SEPARATOR_30) { + state = State.READ_FRAME_COUNTER; + // 4 bytes for frame counter + changeToNextStateAt += 4; + } else { + logger.debug("Missing separator (0x30). Dropping telegram."); + state = State.WAITING_FOR_START_BYTE; + } + break; + case READ_FRAME_COUNTER: + iv.put(rawInput); + if (currentBytePosition >= changeToNextStateAt) { + state = State.READ_PAYLOAD; + changeToNextStateAt += dataLength - ADD_LENGTH; + } + break; + case READ_PAYLOAD: + cipherText.put(rawInput); + if (currentBytePosition >= changeToNextStateAt) { + state = State.READ_GCM_TAG; + changeToNextStateAt += GCM_TAG_LENGTH; + } + break; + case READ_GCM_TAG: + // All input has been read. + cipherText.put(rawInput); + if (currentBytePosition >= changeToNextStateAt) { + state = State.DONE_READING_TELEGRAM; + } + break; + } + if (state == State.DONE_READING_TELEGRAM) { + state = State.WAITING_FOR_START_BYTE; + return true; + } + return false; + } + + private void processCompleted() { + byte[] plainText = decrypt(); + + reset(); + if (plainText == null) { + telegramListener + .telegramReceived(new P1Telegram(Collections.emptyList(), TelegramState.INVALID_ENCRYPTION_KEY)); + } else { + parser.parse(plainText, plainText.length); + } + } + + /** + * Decrypts the collected message. + * + * @return the decrypted message + */ + private byte @Nullable [] decrypt() { + try { + if (secretKeySpec != null) { + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new GCMParameterSpec(GCM_BITS, iv.array())); + cipher.updateAAD(ADD_DECODED); + return cipher.doFinal(cipherText.array(), 0, cipherText.position()); + } + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException + | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { + logger.warn("Decrypting smarty telegram failed: ", e); + } + return null; + } + + @Override + public void reset() { + parser.reset(); + state = State.WAITING_FOR_START_BYTE; + iv.clear(); + cipherText.clear(); + currentBytePosition = 0; + changeToNextStateAt = 0; + dataLength = 0; + } + + @Override + public void setLenientMode(boolean lenientMode) { + this.lenientMode = lenientMode; + parser.setLenientMode(lenientMode); + } +} diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRSerialConnector.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRSerialConnector.java index 582db9d9b9fd4..ebd271351ae8f 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRSerialConnector.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRSerialConnector.java @@ -233,7 +233,7 @@ public void setSerialPortParams(DSMRSerialSettings portSettings) { } } catch (UnsupportedCommOperationException e) { logger.debug( - "Port does not support requested port settings (invalid dsmr:portsettings parameter?): {}", + "Port does {} not support requested port settings (invalid dsmr:portsettings parameter?): {}", serialPortName, portSettings); dsmrConnectorListener.handleErrorEvent(DSMRConnectorErrorEvent.NOT_COMPATIBLE); } diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemDate.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemDate.java index 94821fb139c52..54141856ca251 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemDate.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemDate.java @@ -20,6 +20,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.DateTimeType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,6 +31,7 @@ * @author M. Volaart - Initial contribution * @author Hilbrand Bouwkamp - Class now a factory instead of data containing class */ +@NonNullByDefault class CosemDate extends CosemValueDescriptor { public static final CosemDate INSTANCE = new CosemDate("timestamp"); diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemDecimal.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemDecimal.java index 49b42e135d376..a09f093ac8af7 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemDecimal.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemDecimal.java @@ -14,6 +14,7 @@ import java.text.ParseException; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.DecimalType; /** @@ -22,6 +23,7 @@ * @author M. Volaart - Initial contribution * @author Hilbrand Bouwkamp - Combined Integer and Double because {@link DecimalType} handles both */ +@NonNullByDefault class CosemDecimal extends CosemValueDescriptor { public static final CosemDecimal INSTANCE = new CosemDecimal(); diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemHexString.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemHexString.java index 7f447af52b89c..9587570f6a9c0 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemHexString.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemHexString.java @@ -14,6 +14,7 @@ import java.text.ParseException; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.StringType; /** @@ -22,6 +23,7 @@ * @author M. Volaart - Initial contribution * @author Hilbrand Bouwkamp - Class now a factory instead of data containing class */ +@NonNullByDefault class CosemHexString extends CosemValueDescriptor { public static final CosemHexString INSTANCE = new CosemHexString(); diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObject.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObject.java index 693fd244eef35..c25caa3857b37 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObject.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObject.java @@ -19,6 +19,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.types.State; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,6 +29,7 @@ * * @author M. Volaart - Initial contribution */ +@NonNullByDefault public class CosemObject { /** diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObjectFactory.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObjectFactory.java index ff1977efc49df..f2f665992f167 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObjectFactory.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObjectFactory.java @@ -18,6 +18,8 @@ import java.util.List; import java.util.Map; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,6 +28,7 @@ * * @author M. Volaart - Initial contribution */ +@NonNullByDefault public class CosemObjectFactory { private final Logger logger = LoggerFactory.getLogger(CosemObjectFactory.class); @@ -84,7 +87,7 @@ public CosemObjectFactory() { * @param cosemStringValues String containing Cosem values * @return CosemObject or null if parsing failed */ - public CosemObject getCosemObject(String obisIdString, String cosemStringValues) { + public @Nullable CosemObject getCosemObject(String obisIdString, String cosemStringValues) { OBISIdentifier obisId; OBISIdentifier reducedObisId; @@ -112,7 +115,7 @@ public CosemObject getCosemObject(String obisIdString, String cosemStringValues) cosemObject = getCosemObjectInternal(obisMsgType, obisId, cosemStringValues); if (cosemObject != null) { logger.trace("Searched reducedObisId {} in the wild card type list, result: {}", reducedObisId, - cosemObject); + cosemObject); obisLookupTableDynamic.put(reducedObisId, obisMsgType); break; } @@ -136,8 +139,8 @@ public CosemObject getCosemObject(String obisIdString, String cosemStringValues) * * @return a CosemObject or null if parsing failed */ - private CosemObject getCosemObjectInternal(CosemObjectType cosemObjectType, OBISIdentifier obisIdentifier, - String cosemStringValues) { + private @Nullable CosemObject getCosemObjectInternal(CosemObjectType cosemObjectType, OBISIdentifier obisIdentifier, + String cosemStringValues) { CosemObject obj = new CosemObject(cosemObjectType, obisIdentifier); try { @@ -147,7 +150,7 @@ private CosemObject getCosemObjectInternal(CosemObjectType cosemObjectType, OBIS return obj; } catch (ParseException pe) { logger.trace("Failed to construct Cosem Object for type {}, values: {}", cosemObjectType, cosemStringValues, - pe); + pe); } return null; } diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObjectType.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObjectType.java index df03d4b676817..0206af77dae51 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObjectType.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObjectType.java @@ -20,7 +20,6 @@ import javax.measure.quantity.Time; -import org.eclipse.smarthome.core.library.types.QuantityType; import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; import org.openhab.binding.dsmr.internal.meter.DSMRMeterConstants; @@ -37,7 +36,7 @@ * * * @author M. Volaart - Initial contribution - * @author Hilbrand Bouwkamp - Cosem subclasses made into factory classes and introduced {@link QuantityType} + * @author Hilbrand Bouwkamp - Cosem subclasses made into factory classes and introduced quantity type */ public enum CosemObjectType { UNKNOWN(new OBISIdentifier(-1, DSMRMeterConstants.UNKNOWN_CHANNEL, -1, -1, -1, null), CosemString.INSTANCE), @@ -136,7 +135,21 @@ public enum CosemObjectType { GJMETER_VALUE_V4(new OBISIdentifier(0, null, 24, 2, 1, null), CosemDate.INSTANCE, CosemQuantity.GIGA_JOULE), /* Generic Meter (DSMR v3 only) */ - GENMETER_VALUE_V3(new OBISIdentifier(0, null, 24, 3, 0, null), CosemDecimal.INSTANCE); + GENMETER_VALUE_V3(new OBISIdentifier(0, null, 24, 3, 0, null), CosemDecimal.INSTANCE), + + /* Additional Luxembourgish Smarty Electricity */ + EMETER_TOTAL_IMPORTED_ENERGY_REGISTER_Q(new OBISIdentifier(1, null, 3, 8, 0, null), CosemQuantity.KILO_VAR_HOUR), + EMETER_TOTAL_EXPORTED_ENERGY_REGISTER_Q(new OBISIdentifier(1, null, 4, 8, 0, null), CosemQuantity.KILO_VAR_HOUR), + // 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_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), + EMETER_INSTANT_REACTIVE_POWER_PRODUCTION_L1(new OBISIdentifier(1, 0, 24, 7, 0, null), CosemQuantity.KILO_VAR), + EMETER_INSTANT_REACTIVE_POWER_PRODUCTION_L2(new OBISIdentifier(1, 0, 44, 7, 0, null), CosemQuantity.KILO_VAR), + EMETER_INSTANT_REACTIVE_POWER_PRODUCTION_L3(new OBISIdentifier(1, 0, 64, 7, 0, null), CosemQuantity.KILO_VAR); /** OBIS reduced identifier */ public final OBISIdentifier obisId; diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemQuantity.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemQuantity.java index 2ff9562aa1f9e..765c8b708cc1e 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemQuantity.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemQuantity.java @@ -24,6 +24,8 @@ import javax.measure.quantity.Power; import javax.measure.quantity.Volume; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.library.types.QuantityType; import org.eclipse.smarthome.core.library.unit.MetricPrefix; import org.eclipse.smarthome.core.library.unit.SIUnits; @@ -36,7 +38,8 @@ * * @param The {@link Quantity} type of the unit of this class */ -class CosemQuantity> extends CosemValueDescriptor> { +@NonNullByDefault +class CosemQuantity> extends CosemValueDescriptor> { public static final CosemQuantity AMPERE = new CosemQuantity<>(SmartHomeUnits.AMPERE); public static final CosemQuantity CUBIC_METRE = new CosemQuantity<>(SIUnits.CUBIC_METRE); @@ -45,6 +48,8 @@ class CosemQuantity> extends CosemValueDescriptor KILO_WATT_HOUR = new CosemQuantity<>(SmartHomeUnits.KILOWATT_HOUR); public static final CosemQuantity VOLT = new CosemQuantity<>(SmartHomeUnits.VOLT); public static final CosemQuantity WATT = new CosemQuantity<>(SmartHomeUnits.WATT); + public static final CosemQuantity KILO_VAR = new CosemQuantity<>(SmartHomeUnits.KILOVAR); + public static final CosemQuantity KILO_VAR_HOUR = new CosemQuantity<>(SmartHomeUnits.KILOVAR_HOUR); /** * Pattern to convert a cosem value to a value that can be parsed by {@link QuantityType}. diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemString.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemString.java index 53bcfe713f35f..424e8a82369fb 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemString.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemString.java @@ -12,6 +12,7 @@ */ package org.openhab.binding.dsmr.internal.device.cosem; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.StringType; /** @@ -20,6 +21,7 @@ * @author M. Volaart - Initial contribution * @author Hilbrand Bouwkamp - Class now a factory instead of data containing class */ +@NonNullByDefault class CosemString extends CosemValueDescriptor { public static final CosemString INSTANCE = new CosemString(); diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemValueDescriptor.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemValueDescriptor.java index a567b4f997a73..45af03244ddea 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemValueDescriptor.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemValueDescriptor.java @@ -14,6 +14,7 @@ import java.text.ParseException; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.types.State; /** @@ -21,6 +22,7 @@ * * @author M. Volaart - Initial contribution */ +@NonNullByDefault abstract class CosemValueDescriptor { /** @@ -30,8 +32,6 @@ abstract class CosemValueDescriptor { /** * Creates a new {@link CosemValueDescriptor} with no unit and a default channel. - * - * @param cosemValueClass the {@link CosemValue} class that the {@link CosemValueDescriptor} represent */ public CosemValueDescriptor() { this(""); diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/OBISIdentifier.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/OBISIdentifier.java index 0d84fd8b4517e..0da24c981168f 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/OBISIdentifier.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/OBISIdentifier.java @@ -17,12 +17,16 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + /** * Class representing an OBISIdentifier * * @author M. Volaart - Initial contribution * @author Hilbrand Bouwkamp - Fix bug in regex pattern. */ +@NonNullByDefault public class OBISIdentifier { /** * String representing a-b:c.d.e.f OBIS ID @@ -36,11 +40,11 @@ public class OBISIdentifier { /* the six individual group values of the OBIS ID */ private int groupA; - private Integer groupB; + private @Nullable Integer groupB; private int groupC; private int groupD; - private int groupE; - private Integer groupF; + private @Nullable Integer groupE; + private @Nullable Integer groupF; /** * Constructs a new OBIS Identifier (A-B:C.D.E.F) @@ -52,7 +56,8 @@ public class OBISIdentifier { * @param groupE E value * @param groupF F value */ - public OBISIdentifier(int groupA, Integer groupB, int groupC, int groupD, int groupE, Integer groupF) { + public OBISIdentifier(int groupA, @Nullable Integer groupB, int groupC, int groupD, @Nullable Integer groupE, + @Nullable Integer groupF) { this.groupA = groupA; this.groupB = groupB; this.groupC = groupC; @@ -78,7 +83,7 @@ public OBISIdentifier(String obisIDString) throws ParseException { // Optional value B if (m.group(4) != null) { - this.groupB = Integer.parseInt(m.group(4)); + this.groupB = Integer.valueOf(m.group(4)); } // Required value C & D @@ -87,12 +92,12 @@ public OBISIdentifier(String obisIDString) throws ParseException { // Optional value E if (m.group(9) != null) { - this.groupE = Integer.parseInt(m.group(9)); + this.groupE = Integer.valueOf(m.group(9)); } // Optional value F if (m.group(11) != null) { - this.groupF = Integer.parseInt(m.group(11)); + this.groupF = Integer.valueOf(m.group(11)); } } else { throw new ParseException("Invalid OBIS identifier:" + obisIDString, 0); @@ -109,7 +114,7 @@ public int getGroupA() { /** * @return the groupB */ - public Integer getGroupB() { + public @Nullable Integer getGroupB() { return groupB; } @@ -130,21 +135,21 @@ public int getGroupD() { /** * @return the groupE */ - public int getGroupE() { + public @Nullable Integer getGroupE() { return groupE; } /** * @return the groupF */ - public Integer getGroupF() { + public @Nullable Integer getGroupF() { return groupF; } @Override public String toString() { - return groupA + "-" + (groupB != null ? (groupB + ":") : "") + groupC + "." + groupD + "." + groupE - + (groupF != null ? ("*" + groupF) : ""); + return groupA + "-" + (groupB == null ? "" : (groupB + ":")) + groupC + "." + groupD + + (groupE == null ? "" : ("." + groupE)) + (groupF == null ? "" : ("*" + groupF)); } /** @@ -156,7 +161,7 @@ public String toString() { * @return true if both OBISIdentifiers match, false otherwise */ @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { OBISIdentifier o; if (other != null && other instanceof OBISIdentifier) { o = (OBISIdentifier) other; @@ -184,7 +189,7 @@ public boolean equals(Object other) { } /** - * Checks whether this OBIS Identifer and the other identifer equals taking the wildcards into account + * Checks whether this OBIS Identifier and the other identifier equals taking the wildcards into account * * @param o OBISIdentifier to compare to * @@ -199,7 +204,9 @@ public boolean equalsWildCard(OBISIdentifier o) { } result &= groupC == o.groupC; result &= groupD == o.groupD; - result &= groupE == o.groupE; + if (groupE != null && o.groupE != null) { + result &= (groupE.equals(o.groupE)); + } if (groupF != null && o.groupF != null) { result &= (groupF.equals(o.groupF)); } @@ -209,15 +216,15 @@ public boolean equalsWildCard(OBISIdentifier o) { @Override public int hashCode() { - return Objects.hash(groupA, ((groupB == null) ? 0 : groupB), groupC, groupD, groupE, - ((groupF == null) ? 0 : groupF)); + return Objects.hash(groupA, (groupB != null ? groupB : 0), groupC, groupD, (groupE != null ? groupE : 0), + (groupF != null ? groupF : 0)); } /** * Returns an reduced OBIS Identifier. This means group F is set to null * (.i.e. not applicable) * - * @return reduced OBIS Identifer + * @return reduced OBIS Identifier */ public OBISIdentifier getReducedOBISIdentifier() { return new OBISIdentifier(groupA, groupB, groupC, groupD, groupE, null); diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/CRC16.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/CRC16.java index 0def3d29981a6..d7bc51fac9a78 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/CRC16.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/CRC16.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.dsmr.internal.device.p1telegram; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * CRC16 implementation. * @@ -22,7 +24,9 @@ * * @author M. Volaart - Initial contribution */ +@NonNullByDefault public class CRC16 { + public enum Polynom { CRC16_IBM(0xA001), // standard CRC-16 x16+x15+x2+1 (CRC-16-IBM) CRC16_IBM_REVERSE(0xC002), // standard reverse x16+x14+x+1 (CRC-16-IBM) diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1Telegram.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1Telegram.java index 09d8f2edf630d..d350eaa9deabb 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1Telegram.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1Telegram.java @@ -12,7 +12,9 @@ */ package org.openhab.binding.dsmr.internal.device.p1telegram; +import java.util.Collections; import java.util.List; +import java.util.Map.Entry; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.dsmr.internal.device.cosem.CosemObject; @@ -41,7 +43,12 @@ public enum TelegramState { /** * DATA_CORRUPTION. The P1 telegram has syntax errors. */ - DATA_CORRUPTION("Received P1 telegram is corrupted"); + DATA_CORRUPTION("Received P1 telegram is corrupted"), + /** + * P1TelegramListener. The smarty telegram was successful received but could not be decoded because of an invalid + * encryption key. + */ + INVALID_ENCRYPTION_KEY("Failed to decrypt P1 telegram due to invalid encryption key"); /** * public accessible state details @@ -61,11 +68,18 @@ private TelegramState(String stateDetails) { private final List cosemObjects; private final TelegramState telegramState; private final String rawTelegram; + private final List> unknownCosemObjects; + + public P1Telegram(List cosemObjects, TelegramState telegramState) { + this(cosemObjects, telegramState, "", Collections.emptyList()); + } - public P1Telegram(List cosemObjects, TelegramState telegramState, String rawTelegram) { + public P1Telegram(List cosemObjects, TelegramState telegramState, String rawTelegram, + List> unknownCosemObjects) { this.cosemObjects = cosemObjects; this.telegramState = telegramState; this.rawTelegram = rawTelegram; + this.unknownCosemObjects = unknownCosemObjects; } /** @@ -76,7 +90,7 @@ public List getCosemObjects() { } /** - * @return The raw telegram data + * @return The raw telegram data. */ public String getRawTelegram() { return rawTelegram; @@ -88,4 +102,11 @@ public String getRawTelegram() { public TelegramState getTelegramState() { return telegramState; } + + /** + * @return The list of CosemObject found in the telegram but not known to the binding + */ + public List> getUnknownCosemObjects() { + return unknownCosemObjects; + } } diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParser.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParser.java index ef266ef6c5cc8..3ea47f207c3d7 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParser.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParser.java @@ -13,8 +13,10 @@ package org.openhab.binding.dsmr.internal.device.p1telegram; import java.nio.charset.StandardCharsets; +import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.List; +import java.util.Map.Entry; import java.util.regex.Pattern; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -34,7 +36,7 @@ * @author Hilbrand Bouwkamp - Removed asynchronous call and some clean up */ @NonNullByDefault -public class P1TelegramParser { +public class P1TelegramParser implements TelegramParser { /** * State of the parser @@ -56,13 +58,13 @@ private enum State { CRC_VALUE } - private final Logger logger = LoggerFactory.getLogger(P1TelegramParser.class); - /** * Pattern for the CRC-code */ private static final String CRC_PATTERN = "[0-9A-Z]{4}"; + private final Logger logger = LoggerFactory.getLogger(P1TelegramParser.class); + /* internal state variables */ /** @@ -73,7 +75,7 @@ private enum State { /** * Current cosem object values buffer. */ - private final StringBuilder cosemObjectValuesString = new StringBuilder(); + private final StringBuilder obisValue = new StringBuilder(); /** * In lenient mode store raw data and log when a complete message is received. @@ -88,7 +90,7 @@ private enum State { /** * CRC calculation helper */ - private CRC16 crc; + private final CRC16 crc; /** * Current state of the P1 telegram parser @@ -108,17 +110,22 @@ private enum State { /** * CosemObjectFactory helper class */ - private CosemObjectFactory factory; + private final CosemObjectFactory factory; /** * Received Cosem Objects in the P1Telegram that is currently received */ - private List cosemObjects = new ArrayList<>(); + private final List cosemObjects = new ArrayList<>(); + + /** + * List of Cosem Object values that are not known to this binding. + */ + private final List> unknownCosemObjects = new ArrayList<>(); /** * Listener for new P1 telegrams */ - private P1TelegramListener telegramListener; + private final P1TelegramListener telegramListener; /** * Creates a new P1TelegramParser @@ -135,17 +142,15 @@ public P1TelegramParser(P1TelegramListener telegramListener) { } /** - * Parses data. If parsing is not ready yet nothing will be returned. If - * parsing fails completely nothing will be returned. If parsing succeeds - * (partial) the received OBIS messages will be returned. + * Parses data. If a complete message is received the message will be passed to the telegramListener. * - * @param data byte data - * @param offset offset tot start in the data buffer + * @param data byte data to parse * @param length number of bytes to parse */ - public void parseData(byte[] data, int offset, int length) { + @Override + public void parse(byte[] data, int length) { if (lenientMode || logger.isTraceEnabled()) { - String rawBlock = new String(data, offset, length, StandardCharsets.UTF_8); + String rawBlock = new String(data, 0, length, StandardCharsets.UTF_8); if (lenientMode) { rawData.append(rawBlock); @@ -154,7 +159,7 @@ public void parseData(byte[] data, int offset, int length) { logger.trace("Raw data: {}, Parser state entering parseData: {}", rawBlock, state); } } - for (int i = offset; i < (offset + length); i++) { + for (int i = 0; i < length; i++) { char c = (char) data[i]; switch (state) { @@ -245,7 +250,7 @@ public void parseData(byte[] data, int offset, int length) { if (logger.isDebugEnabled()) { logger.trace("received CRC value: {}, calculated CRC value: 0x{}", crcValue, - String.format("%04X", calculatedCRC)); + String.format("%04X", calculatedCRC)); } if (crcP1Telegram != calculatedCRC) { logger.trace("CRC value does not match, p1 Telegram failed"); @@ -256,9 +261,8 @@ public void parseData(byte[] data, int offset, int length) { telegramState = TelegramState.CRC_ERROR; } } - telegramListener.telegramReceived( - new P1Telegram(new ArrayList<>(cosemObjects), telegramState, rawData.toString())); - setState(State.WAIT_FOR_START); + telegramListener.telegramReceived(constructTelegram()); + reset(); if (c == '/') { /* * Immediately proceed to the next state (robust implementation for meter that do not follow @@ -275,9 +279,17 @@ public void parseData(byte[] data, int offset, int length) { logger.trace("State after parsing: {}", state); } - /** - * Reset the current telegram state - */ + private P1Telegram constructTelegram() { + final List cosemObjectsCopy = new ArrayList<>(cosemObjects); + + if (lenientMode) { + return new P1Telegram(cosemObjectsCopy, telegramState, rawData.toString(), unknownCosemObjects); + } else { + return new P1Telegram(cosemObjectsCopy, telegramState); + } + } + + @Override public void reset() { setState(State.WAIT_FOR_START); } @@ -314,11 +326,11 @@ private void handleCharacter(char c) { crc.processByte((byte) c); break; case DATA_OBIS_VALUE: - cosemObjectValuesString.append(c); + obisValue.append(c); crc.processByte((byte) c); break; case DATA_OBIS_VALUE_END: - cosemObjectValuesString.append(c); + obisValue.append(c); crc.processByte((byte) c); break; case CRC_VALUE: @@ -339,22 +351,22 @@ private void handleCharacter(char c) { */ private void clearInternalData() { obisId.setLength(0); - cosemObjectValuesString.setLength(0); + obisValue.setLength(0); rawData.setLength(0); crcValue.setLength(0); crc.initialize(); cosemObjects.clear(); + unknownCosemObjects.clear(); } /** * Clears all the current OBIS data. I.e. * - current OBIS identifier * - current OBIS value - * - current OBIS data object */ private void clearObisData() { obisId.setLength(0); - cosemObjectValuesString.setLength(0); + obisValue.setLength(0); } /** @@ -364,17 +376,23 @@ private void storeCurrentCosemObject() { String obisIdString = obisId.toString(); if (!obisIdString.isEmpty()) { - CosemObject cosemObject = factory.getCosemObject(obisIdString, cosemObjectValuesString.toString()); + final String obisValueString = obisValue.toString(); + CosemObject cosemObject = factory.getCosemObject(obisIdString, obisValueString); - if (cosemObject != null) { + if (cosemObject == null) { + if (lenientMode) { + unknownCosemObjects.add(new SimpleEntry(obisIdString, obisValueString)); + } + } else { logger.trace("Adding {} to list of Cosem Objects", cosemObject); cosemObjects.add(cosemObject); } } + clearObisData(); } /** - * @param state the new state to set + * @param newState the new state to set */ private void setState(State newState) { synchronized (state) { @@ -392,12 +410,10 @@ private void setState(State newState) { // If the current state is CRLF we are processing the header and don't have a cosem object yet if (state != State.CRLF) { storeCurrentCosemObject(); - clearObisData(); } break; case CRC_VALUE: storeCurrentCosemObject(); - clearObisData(); break; default: break; @@ -406,9 +422,7 @@ private void setState(State newState) { } } - /** - * @param lenientMode the lenientMode to set - */ + @Override public void setLenientMode(boolean lenientMode) { this.lenientMode = lenientMode; } diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/TelegramParser.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/TelegramParser.java new file mode 100644 index 0000000000000..c9671c9e3f38b --- /dev/null +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/TelegramParser.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2010-2019 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.dsmr.internal.device.p1telegram; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * + * @author Hilbrand Bouwkamp - Initial contribution + */ +@NonNullByDefault +public interface TelegramParser { + + /** + * + * @param data byte data to parse + * @param length number of bytes to parse + */ + void parse(byte[] data, int length); + + /** + * Reset the current telegram state. + */ + default void reset() { + }; + + /** + * @param lenientMode the lenientMode to set + */ + default void setLenientMode(boolean lenientMode) { + }; +} diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRBridgeDiscoveryService.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRBridgeDiscoveryService.java index b243cf3067f4c..5a8eb9f81b515 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRBridgeDiscoveryService.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRBridgeDiscoveryService.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.dsmr.internal.discovery; +import static org.openhab.binding.dsmr.internal.DSMRBindingConstants.*; + import java.util.HashMap; import java.util.List; import java.util.Map; @@ -24,16 +26,18 @@ import org.eclipse.smarthome.config.discovery.DiscoveryService; import org.eclipse.smarthome.core.i18n.LocaleProvider; import org.eclipse.smarthome.core.i18n.TranslationProvider; +import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.ThingUID; import org.eclipse.smarthome.io.transport.serial.SerialPortIdentifier; import org.eclipse.smarthome.io.transport.serial.SerialPortManager; -import org.openhab.binding.dsmr.internal.DSMRBindingConstants; import org.openhab.binding.dsmr.internal.device.DSMRDeviceRunnable; import org.openhab.binding.dsmr.internal.device.DSMREventListener; import org.openhab.binding.dsmr.internal.device.DSMRSerialAutoDevice; +import org.openhab.binding.dsmr.internal.device.DSMRTelegramListener; import org.openhab.binding.dsmr.internal.device.connector.DSMRConnectorErrorEvent; import org.openhab.binding.dsmr.internal.device.cosem.CosemObject; import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram; +import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram.TelegramState; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; @@ -105,15 +109,18 @@ protected void startScan() { if (portIdentifier.isCurrentlyOwned()) { logger.trace("Possible port to check:{}, owned:{} by:{}", currentScannedPortName, portIdentifier.isCurrentlyOwned(), portIdentifier.getCurrentOwner()); - if (DSMRBindingConstants.DSMR_PORT_NAME.equals(portIdentifier.getCurrentOwner())) { + if (DSMR_PORT_NAME.equals(portIdentifier.getCurrentOwner())) { logger.debug("The port {} is owned by this binding. If no DSMR meters will be found it " + "might indicate the port is locked by an older instance of this binding. " + "Restart the system to unlock the port.", currentScannedPortName); } } else { logger.debug("Start discovery on serial port: {}", currentScannedPortName); - DSMRSerialAutoDevice device = new DSMRSerialAutoDevice(serialPortManager, portIdentifier.getName(), - this, scheduler, BAUDRATE_SWITCH_TIMEOUT_SECONDS); + // + final DSMRTelegramListener telegramListener = new DSMRTelegramListener(""); + final DSMRSerialAutoDevice device = new DSMRSerialAutoDevice(serialPortManager, + portIdentifier.getName(), this, telegramListener, scheduler, + BAUDRATE_SWITCH_TIMEOUT_SECONDS); device.setLenientMode(true); currentScannedDevice = new DSMRDeviceRunnable(device, this); currentScannedDevice.run(); @@ -145,7 +152,7 @@ private void stopSerialPortScan() { /** * Handle if telegrams are received. * - * If there are cosem objects received a new bridge will we discovered + * If there are cosem objects received a new bridge will we discovered. * * @param telegram the received telegram */ @@ -156,35 +163,39 @@ public void handleTelegramReceived(P1Telegram telegram) { if (logger.isDebugEnabled()) { logger.debug("[{}] Received {} cosemObjects", currentScannedPortName, cosemObjects.size()); } - if (!cosemObjects.isEmpty()) { - bridgeDiscovered(telegram); + if (telegram.getTelegramState() == TelegramState.INVALID_ENCRYPTION_KEY) { + bridgeDiscovered(THING_TYPE_SMARTY_BRIDGE); + stopSerialPortScan(); + } else if (!cosemObjects.isEmpty()) { + ThingUID bridgeThingUID = bridgeDiscovered(THING_TYPE_DSMR_BRIDGE); + meterDetector.detectMeters(telegram).getKey().forEach(m -> meterDiscovered(m, bridgeThingUID)); stopSerialPortScan(); } } /** + * Creates a bridge. * - * Therefore this method will always return true - * - * @return true if bridge is accepted, false otherwise + * @return The {@link ThingUID} of the newly created bridge */ - private boolean bridgeDiscovered(P1Telegram telegram) { - ThingUID thingUID = new ThingUID(DSMRBindingConstants.THING_TYPE_DSMR_BRIDGE, - Integer.toHexString(currentScannedPortName.hashCode())); + private ThingUID bridgeDiscovered(ThingTypeUID bridgeThingTypeUID) { + ThingUID thingUID = new ThingUID(bridgeThingTypeUID, Integer.toHexString(currentScannedPortName.hashCode())); + final boolean smarty = THING_TYPE_SMARTY_BRIDGE.equals(bridgeThingTypeUID); + final String label = String.format("@text/thing-type.dsmr.%s.label", smarty ? "smartyBridge" : "dsmrBridge"); // Construct the configuration for this meter Map properties = new HashMap<>(); - properties.put("serialPort", currentScannedPortName); - - DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID) - .withThingType(DSMRBindingConstants.THING_TYPE_DSMR_BRIDGE).withProperties(properties) - .withLabel("@text/thing-type.dsmr.dsmrBridge.label").build(); + properties.put(CONFIGURATION_SERIAL_PORT, currentScannedPortName); + if (smarty) { + properties.put(CONFIGURATION_DECRYPTION_KEY, CONFIGURATION_DECRYPTION_KEY_EMPTY); + } + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(bridgeThingTypeUID) + .withProperties(properties).withLabel(label).build(); logger.debug("[{}] discovery result:{}", currentScannedPortName, discoveryResult); thingDiscovered(discoveryResult); - meterDetector.detectMeters(telegram).getKey().forEach(m -> meterDiscovered(m, thingUID)); - return true; + return thingUID; } @Override diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRDiscoveryService.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRDiscoveryService.java index 20920e138d08f..fabee46834ac8 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRDiscoveryService.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRDiscoveryService.java @@ -35,7 +35,7 @@ @NonNullByDefault public abstract class DSMRDiscoveryService extends AbstractDiscoveryService { /** - * Timeout for Discovery timeout + * Timeout for discovery time. */ private static final int DSMR_DISCOVERY_TIMEOUT_SECONDS = 60; diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDiscoveryService.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDiscoveryService.java index c986f5256b410..a53232764cdbd 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDiscoveryService.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDiscoveryService.java @@ -100,11 +100,19 @@ protected void verifyUnregisteredCosemObjects(P1Telegram telegram, reportUnregisteredMeters(); } else { reportUnrecognizedCosemObjects(undetectedCosemObjects); - logger.info("There are some unrecognized cosem values in the data received from the meter," + logger.info("There are unrecognized cosem values in the data received from the meter," + " which means some meters might not be detected. Please report your raw data as reference: {}", telegram.getRawTelegram()); } } + if (!telegram.getUnknownCosemObjects().isEmpty()) { + logger.info("There are unrecognized cosem values in the data received from the meter," + + " which means you have values that can't be read by a channel: {}. Please report them and your raw data as reference: {}", + telegram.getUnknownCosemObjects().stream() + .map(e -> String.format("obis id:{}, value:{}", e.getKey(), e.getValue())) + .collect(Collectors.joining(", ")), + telegram.getRawTelegram()); + } } /** diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRBridgeHandler.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRBridgeHandler.java index bafdaffe6f81b..d42452fd493c2 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRBridgeHandler.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRBridgeHandler.java @@ -12,13 +12,14 @@ */ package org.openhab.binding.dsmr.internal.handler; +import static org.openhab.binding.dsmr.internal.DSMRBindingConstants.THING_TYPE_SMARTY_BRIDGE; + import java.util.ArrayList; import java.util.List; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.thing.Bridge; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.ThingStatus; @@ -32,6 +33,7 @@ import org.openhab.binding.dsmr.internal.device.DSMREventListener; import org.openhab.binding.dsmr.internal.device.DSMRFixedConfigDevice; import org.openhab.binding.dsmr.internal.device.DSMRSerialAutoDevice; +import org.openhab.binding.dsmr.internal.device.DSMRTelegramListener; import org.openhab.binding.dsmr.internal.device.connector.DSMRConnectorErrorEvent; import org.openhab.binding.dsmr.internal.device.connector.DSMRSerialSettings; import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram; @@ -70,23 +72,23 @@ public class DSMRBridgeHandler extends BaseBridgeHandler implements DSMREventLis /** * The dsmrDevice managing the connection and handling telegrams. */ - private @Nullable DSMRDevice dsmrDevice; + private @NonNullByDefault({}) DSMRDevice dsmrDevice; /** * Long running process that controls the DSMR device connection. */ - private @Nullable DSMRDeviceRunnable dsmrDeviceRunnable; + private @NonNullByDefault({}) DSMRDeviceRunnable dsmrDeviceRunnable; /** * Thread for {@link DSMRDeviceRunnable}. A thread is used because the {@link DSMRDeviceRunnable} is a blocking * process that runs as long as the thing is not disposed. */ - private @Nullable Thread dsmrDeviceThread; + private @NonNullByDefault({}) Thread dsmrDeviceThread; /** * Watchdog to check if messages received and restart if necessary. */ - private @Nullable ScheduledFuture watchdog; + private @NonNullByDefault({}) ScheduledFuture watchdog; /** * Number of nanoseconds after which a timeout is triggered when no messages received. @@ -98,6 +100,8 @@ public class DSMRBridgeHandler extends BaseBridgeHandler implements DSMREventLis */ private volatile long telegramReceivedTimeNanos; + private final boolean smartyMeter; + /** * Constructor * @@ -107,6 +111,7 @@ public class DSMRBridgeHandler extends BaseBridgeHandler implements DSMREventLis public DSMRBridgeHandler(Bridge bridge, SerialPortManager serialPortManager) { super(bridge); this.serialPortManager = serialPortManager; + smartyMeter = THING_TYPE_SMARTY_BRIDGE.equals(bridge.getThingTypeUID()); } /** @@ -128,44 +133,49 @@ public void handleCommand(ChannelUID channelUID, Command command) { */ @Override public void initialize() { - DSMRDeviceConfiguration deviceConfig = getConfigAs(DSMRDeviceConfiguration.class); + final DSMRDeviceConfiguration deviceConfig = getConfigAs(DSMRDeviceConfiguration.class); + + if (smartyMeter && (deviceConfig.decryptionKey == null || deviceConfig.decryptionKey.length() != 32)) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/error.configuration.invalidsmartykey"); + return; + } logger.trace("Using configuration {}", deviceConfig); updateStatus(ThingStatus.UNKNOWN); receivedTimeoutNanos = TimeUnit.SECONDS.toNanos(deviceConfig.receivedTimeout); - try { - DSMRDevice dsmrDevice = createDevice(deviceConfig); - resetLastReceivedState(); - this.dsmrDevice = dsmrDevice; // otherwise Eclipse will give a null pointer error on the next line :-( - dsmrDeviceRunnable = new DSMRDeviceRunnable(dsmrDevice, this); - dsmrDeviceThread = new Thread(dsmrDeviceRunnable); - dsmrDeviceThread.start(); - watchdog = scheduler.scheduleWithFixedDelay(this::alive, receivedTimeoutNanos, receivedTimeoutNanos, + final DSMRDevice dsmrDevice = createDevice(deviceConfig); + resetLastReceivedState(); + this.dsmrDevice = dsmrDevice; // otherwise Eclipse will give a null pointer error on the next line :-( + dsmrDeviceRunnable = new DSMRDeviceRunnable(dsmrDevice, this); + dsmrDeviceThread = new Thread(dsmrDeviceRunnable); + dsmrDeviceThread.start(); + watchdog = scheduler.scheduleWithFixedDelay(this::alive, receivedTimeoutNanos, receivedTimeoutNanos, TimeUnit.NANOSECONDS); - } catch (IllegalArgumentException e) { - logger.debug("Incomplete configuration: {}", deviceConfig); - - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "@text/error.configuration.incomplete"); - } } /** * Creates the {@link DSMRDevice} that corresponds with the user specified configuration. * * @param deviceConfig device configuration - * @return Specific {@link DSMRDevice} instance or throws an {@link IllegalArgumentException} if no valid - * configuration was set. + * @return Specific {@link DSMRDevice} instance */ private DSMRDevice createDevice(DSMRDeviceConfiguration deviceConfig) { - DSMRDevice dsmrDevice; + final DSMRDevice dsmrDevice; - if (deviceConfig.isSerialFixedSettings()) { + if (smartyMeter) { dsmrDevice = new DSMRFixedConfigDevice(serialPortManager, deviceConfig.serialPort, - DSMRSerialSettings.getPortSettingsFromConfiguration(deviceConfig), this); + DSMRSerialSettings.HIGH_SPEED_SETTINGS, this, new DSMRTelegramListener(deviceConfig.decryptionKey)); } else { - dsmrDevice = new DSMRSerialAutoDevice(serialPortManager, deviceConfig.serialPort, this, scheduler, - deviceConfig.receivedTimeout); + final DSMRTelegramListener telegramListener = new DSMRTelegramListener(); + + if (deviceConfig.isSerialFixedSettings()) { + dsmrDevice = new DSMRFixedConfigDevice(serialPortManager, deviceConfig.serialPort, + DSMRSerialSettings.getPortSettingsFromConfiguration(deviceConfig), this, telegramListener); + } else { + dsmrDevice = new DSMRSerialAutoDevice(serialPortManager, deviceConfig.serialPort, this, + telegramListener, scheduler, deviceConfig.receivedTimeout); + } } return dsmrDevice; } @@ -204,7 +214,7 @@ private void alive() { if (deltaLastReceived > receivedTimeoutNanos) { logger.debug("No data received for {} seconds, restarting port if possible.", - TimeUnit.NANOSECONDS.toSeconds(deltaLastReceived)); + TimeUnit.NANOSECONDS.toSeconds(deltaLastReceived)); if (dsmrDeviceRunnable != null) { dsmrDeviceRunnable.restart(); } @@ -230,7 +240,7 @@ private void resetLastReceivedState() { public synchronized void handleTelegramReceived(P1Telegram telegram) { if (telegram.getCosemObjects().isEmpty()) { logger.debug("Parsing worked but something went wrong, so there were no CosemObjects:{}", - telegram.getTelegramState().stateDetails); + telegram.getTelegramState().stateDetails); deviceOffline(ThingStatusDetail.COMMUNICATION_ERROR, telegram.getTelegramState().stateDetails); } else { resetLastReceivedState(); @@ -255,7 +265,7 @@ private void meterValueReceived(P1Telegram telegram) { getThing().getThings().forEach(child -> { if (logger.isTraceEnabled()) { logger.trace("Update child:{} with {} objects", child.getThingTypeUID().getId(), - telegram.getCosemObjects().size()); + telegram.getCosemObjects().size()); } DSMRMeterHandler dsmrMeterHandler = (DSMRMeterHandler) child.getHandler(); diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRMeterHandler.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRMeterHandler.java index d9c75b34f18e1..add2cf8818055 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRMeterHandler.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRMeterHandler.java @@ -99,17 +99,17 @@ public void initialize() { meterType = DSMRMeterType.valueOf(getThing().getThingTypeUID().getId().toUpperCase()); } catch (IllegalArgumentException iae) { logger.warn( - "{} could not be initialized due to an invalid meterType {}. Delete this Thing if the problem persists.", - getThing(), getThing().getThingTypeUID().getId().toUpperCase()); + "{} could not be initialized due to an invalid meterType {}. Delete this Thing if the problem persists.", + getThing(), getThing().getThingTypeUID().getId().toUpperCase()); updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.CONFIGURATION_ERROR, - "@text/error.configuration.invalidmetertype"); + "@text/error.configuration.invalidmetertype"); return; } DSMRMeterConfiguration meterConfig = getConfigAs(DSMRMeterConfiguration.class); DSMRMeterDescriptor meterDescriptor = new DSMRMeterDescriptor(meterType, meterConfig.channel); meter = new DSMRMeter(meterDescriptor); meterWatchdog = scheduler.scheduleWithFixedDelay(this::updateState, meterConfig.refresh, meterConfig.refresh, - TimeUnit.SECONDS); + TimeUnit.SECONDS); updateStatus(ThingStatus.UNKNOWN); } @@ -158,7 +158,7 @@ private synchronized void updateState() { @Override public void telegramReceived(P1Telegram telegram) { lastReceivedValues = Collections.emptyList(); - DSMRMeter localMeter = meter; + final DSMRMeter localMeter = meter; if (localMeter == null) { return; @@ -183,7 +183,7 @@ public void telegramReceived(P1Telegram telegram) { @Override public synchronized void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) { if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE - && getThing().getStatusInfo().getStatusDetail() == ThingStatusDetail.BRIDGE_OFFLINE) { + && getThing().getStatusInfo().getStatusDetail() == ThingStatusDetail.BRIDGE_OFFLINE) { // Set status to offline --> Thing will become online after receiving meter values setDeviceOffline(ThingStatusDetail.NONE, null); } else if (bridgeStatusInfo.getStatus() == ThingStatus.OFFLINE) { diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterConfiguration.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterConfiguration.java index 7931e843aa630..9a4efe1425eb4 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterConfiguration.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterConfiguration.java @@ -12,12 +12,15 @@ */ package org.openhab.binding.dsmr.internal.meter; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * This class describes the configuration for a meter. * * @author M. Volaart - Initial contribution * @author Hilbrand Bouwkamp - Added refresh field */ +@NonNullByDefault public class DSMRMeterConfiguration { /** * M-Bus channel diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterType.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterType.java index bad5fe98a37fc..2ec3ca428ce41 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterType.java +++ b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterType.java @@ -268,11 +268,34 @@ public enum DSMRMeterType { /** DSMR V5.0 Slave Electricity meter */ SLAVE_ELECTRICITY_V5_0(DSMRMeterKind.SLAVE_ELECTRICITY1, CosemObjectType.METER_EQUIPMENT_IDENTIFIER, CosemObjectType.METER_DEVICE_TYPE, CosemObjectType.METER_EQUIPMENT_IDENTIFIER, - CosemObjectType.EMETER_VALUE); + CosemObjectType.EMETER_VALUE), + + /** Luxembourg "Smarty" V1.0 Electricity meter */ + ELECTRICITY_SMARTY_V1_0(DSMRMeterKind.MAIN_ELECTRICITY, CosemObjectType.EMETER_EQUIPMENT_IDENTIFIER_V2_X, + new CosemObjectType[] { + CosemObjectType.EMETER_EQUIPMENT_IDENTIFIER_V2_X, CosemObjectType.EMETER_DELIVERY_TARIFF0, + CosemObjectType.EMETER_PRODUCTION_TARIFF0, CosemObjectType.EMETER_TOTAL_IMPORTED_ENERGY_REGISTER_Q, + CosemObjectType.EMETER_TOTAL_EXPORTED_ENERGY_REGISTER_Q, CosemObjectType.EMETER_ACTUAL_DELIVERY, + CosemObjectType.EMETER_ACTUAL_PRODUCTION, CosemObjectType.EMETER_ACTUAL_REACTIVE_DELIVERY, + CosemObjectType.EMETER_ACTUAL_REACTIVE_PRODUCTION, CosemObjectType.EMETER_ACTIVE_THRESHOLD_SMAX, + CosemObjectType.EMETER_SWITCH_POSITION }, + new CosemObjectType[] { + CosemObjectType.EMETER_POWER_FAILURES, CosemObjectType.EMETER_VOLTAGE_SAGS_L1, + CosemObjectType.EMETER_VOLTAGE_SAGS_L2, CosemObjectType.EMETER_VOLTAGE_SAGS_L3, + CosemObjectType.EMETER_VOLTAGE_SWELLS_L1, CosemObjectType.EMETER_VOLTAGE_SWELLS_L2, + CosemObjectType.EMETER_VOLTAGE_SWELLS_L3, CosemObjectType.EMETER_INSTANT_CURRENT_L1, + CosemObjectType.EMETER_INSTANT_CURRENT_L2, CosemObjectType.EMETER_INSTANT_CURRENT_L3, + CosemObjectType.EMETER_INSTANT_POWER_DELIVERY_L1, CosemObjectType.EMETER_INSTANT_POWER_DELIVERY_L2, + CosemObjectType.EMETER_INSTANT_POWER_DELIVERY_L3, CosemObjectType.EMETER_INSTANT_POWER_PRODUCTION_L1, + CosemObjectType.EMETER_INSTANT_POWER_PRODUCTION_L2, CosemObjectType.EMETER_INSTANT_POWER_PRODUCTION_L3, + 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, + }); // @formatter:on public static final Set METER_THING_TYPES = Arrays.asList(DSMRMeterType.values()).stream() - .map(DSMRMeterType::getThingTypeUID).collect(Collectors.toSet()); + .map(DSMRMeterType::getThingTypeUID).collect(Collectors.toSet()); private final Logger logger = LoggerFactory.getLogger(DSMRMeterType.class); @@ -304,22 +327,25 @@ public enum DSMRMeterType { /** * Creates a new enum * - * @param channelKey - * String containing the channel configuration for this meter + * @param meterKind kind of meter + * @param cosemObjectTypeMeterId identifier cosem object + * @param requiredCosemObjects list of objects that are present in this meter type */ DSMRMeterType(DSMRMeterKind meterKind, CosemObjectType cosemObjectTypeMeterId, - CosemObjectType... requiredCosemObjects) { + CosemObjectType... requiredCosemObjects) { this(meterKind, cosemObjectTypeMeterId, requiredCosemObjects, new CosemObjectType[0]); } /** * Creates a new enum * - * @param channelKey - * String containing the channel configuration for this meter + * @param meterKind kind of meter + * @param cosemObjectTypeMeterId identifier cosem object + * @param requiredCosemObjects list of objects that are present in this meter type + * @param optionalCosemObjects list of objects that are optional present in this meter type */ DSMRMeterType(DSMRMeterKind meterKind, CosemObjectType cosemObjectTypeMeterId, - CosemObjectType[] requiredCosemObjects, CosemObjectType[] optionalCosemObjects) { + CosemObjectType[] requiredCosemObjects, CosemObjectType[] optionalCosemObjects) { this.meterKind = meterKind; this.cosemObjectTypeMeterId = cosemObjectTypeMeterId; this.requiredCosemObjects = requiredCosemObjects; @@ -328,7 +354,7 @@ public enum DSMRMeterType { supportedCosemObjects = new CosemObjectType[requiredCosemObjects.length + optionalCosemObjects.length]; System.arraycopy(requiredCosemObjects, 0, supportedCosemObjects, 0, requiredCosemObjects.length); System.arraycopy(optionalCosemObjects, 0, supportedCosemObjects, requiredCosemObjects.length, - optionalCosemObjects.length); + optionalCosemObjects.length); } /**